Changeset 2922 for trunk/sources/HeuristicLab.PluginInfrastructure
- Timestamp:
- 03/03/10 18:08:26 (15 years ago)
- Location:
- trunk/sources/HeuristicLab.PluginInfrastructure
- Files:
-
- 14 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/DeploymentService/PluginDescription.cs
r2860 r2922 45 45 46 46 #region IPluginDescription Members 47 47 public string Description { 48 get { return string.Empty; } 49 } 48 50 49 51 [Obsolete] … … 63 65 64 66 #endregion 67 68 public override string ToString() { 69 return Name + " " + Version; 70 } 65 71 } 66 72 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/InstallationManager.cs
r2811 r2922 28 28 using System.ComponentModel; 29 29 using System.Reflection; 30 using ICSharpCode.SharpZipLib.Zip; 30 31 31 32 namespace HeuristicLab.PluginInfrastructure.Advanced { … … 40 41 internal event EventHandler<PluginInfrastructureEventArgs> PluginInstalled; 41 42 42 43 public IEnumerable<PluginDescription> Plugins {44 get { return pluginManager.Plugins; }45 }46 47 43 private string pluginDir; 48 private string updateLocationUrl;49 private PluginManager pluginManager;50 44 public InstallationManager(string pluginDir) { 51 45 this.pluginDir = pluginDir; 52 this.updateLocationUrl = "http://localhost:59253/UpdateLocation.svc"; 53 this.pluginManager = new PluginManager(pluginDir); 54 this.pluginManager.DiscoverAndCheckPlugins(); 55 } 56 57 public IEnumerable<string> Show(IEnumerable<string> pluginNames) { 58 foreach (PluginDescription desc in GetPluginDescriptions(pluginNames)) { 59 yield return GetInformation(desc); 60 } 61 } 62 63 internal string GetInformation(string pluginName) { 64 return GetInformation(GetPluginDescription(pluginName)); 65 } 66 67 private string GetInformation(PluginDescription desc) { 68 StringBuilder builder = new StringBuilder(); 69 builder.Append("Name: ").AppendLine(desc.Name); 70 builder.Append("Version: ").AppendLine(desc.Version.ToString()); 71 builder.AppendLine("Description:").AppendLine(desc.Description); 72 if (!string.IsNullOrEmpty(desc.ContactName)) { 73 builder.Append("Contact: ").Append(desc.ContactName).Append(", ").AppendLine(desc.ContactEmail); 74 } 75 builder.AppendLine("This plugin is " + desc.PluginState.ToString().ToLowerInvariant() + "."); 76 builder.AppendLine("Files: "); 77 foreach (var file in desc.Files) { 78 builder.AppendLine(file.Type + " " + file.Name); 79 } 80 builder.AppendLine().AppendLine("Directly depends on:"); 81 if (desc.Dependencies.Count() == 0) builder.AppendLine("None"); 82 foreach (var dependency in desc.Dependencies) { 83 builder.AppendLine(dependency.Name + " " + dependency.Version); 84 } 85 builder.AppendLine().AppendFormat("Plugins directly dependent on {0}:", desc.Name).AppendLine(); 86 var dependents = from x in pluginManager.Plugins 87 where x.Dependencies.Contains(desc) 88 select x; 89 if (dependents.Count() == 0) builder.AppendLine("None"); 90 foreach (var dependent in dependents) { 91 builder.AppendLine(dependent.Name + " " + dependent.Version); 92 } 93 builder.AppendLine(); 94 if (desc.PluginState == PluginState.Disabled) { 95 builder.AppendLine(DetermineProblem(desc)); 96 } 97 98 return builder.ToString(); 99 } 100 101 private static string DetermineProblem(PluginDescription desc) { 102 // either any file is missing 103 StringBuilder builder = new StringBuilder(); 104 builder.AppendLine("Problem report:"); 105 builder.AppendLine(desc.LoadingErrorInformation); 106 return builder.ToString(); 107 } 108 109 private static IEnumerable<string> GetDeclaredDependencies(PluginDescription desc) { 110 var plugin = ApplicationManager.GetInstances<IPlugin>(desc).Single(); 111 return plugin.GetType().GetCustomAttributes(typeof(PluginDependencyAttribute), false).Cast<PluginDependencyAttribute>().Select(x => x.Dependency); 112 } 113 114 private PluginDescription GetPluginDescription(string pluginName) { 115 var exactMatch = from pluginDesc in pluginManager.Plugins 116 where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase) 117 select pluginDesc; 118 var inexactMatch = from pluginDesc in pluginManager.Plugins 119 where MatchPluginNameInexact(pluginName, pluginDesc.Name) 120 select pluginDesc; 121 return exactMatch.Count() > 0 ? exactMatch.Single() : inexactMatch.First(); 122 } 123 124 private IEnumerable<PluginDescription> GetPluginDescriptions(IEnumerable<string> pluginNames) { 125 return from pluginName in pluginNames 126 select GetPluginDescription(pluginName); 127 } 128 129 private static bool MatchPluginNameInexact(string similarName, string actualName) { 130 return 131 // Core-3.2 == HeuristicLab.Core-3.2 132 actualName.Equals("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase) || 133 // HeuristicLab.Core == HeuristicLab.Core-3.2 (this should be save because we checked for exact matches first) 134 (Math.Abs(actualName.Length - similarName.Length) <= 4 && actualName.StartsWith(similarName, StringComparison.InvariantCultureIgnoreCase)) || 135 // Core == HeuristicLab.Core-3.2 136 (Math.Abs(actualName.Length - similarName.Length) <= 17 && actualName.StartsWith("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase)); 137 } 138 139 public void Install(IEnumerable<string> pluginNames) { 140 throw new NotImplementedException(); 141 //IEnumerable<PluginInformation> pluginsToInstall; 142 //using (UpdateLocationClient updateLocation = new UpdateLocationClient()) { 143 // pluginsToInstall = from pluginName in pluginNames 144 // from matchingPlugin in updateLocation.GetAvailablePluginsByName(pluginName) 145 // select matchingPlugin; 146 147 // var args = new PluginInfrastructureCancelEventArgs("Installing", pluginsToInstall); 148 // OnPreInstall(args); 149 // foreach (var pluginInfo in pluginsToInstall) { 150 // var s = updateLocation.GetPluginFiles(pluginInfo); 151 // Console.WriteLine("Downloading: {0} {1} {2}", pluginInfo.Name, pluginInfo.Version, pluginInfo.BuildDate); 152 // } 153 //} 154 //OnInstalled(new PluginInfrastructureEventArgs("Installed", pluginsToInstall)); 155 } 156 157 //private static PluginInformation GetMatchingPluginInformation(string pluginName, IEnumerable<PluginInformation> plugins) { 158 // var exactMatch = from pluginDesc in plugins 46 } 47 48 //public IEnumerable<string> Show(IEnumerable<string> pluginNames) { 49 // foreach (PluginDescription desc in GetPluginDescriptions(pluginNames)) { 50 // yield return GetInformation(desc); 51 // } 52 //} 53 54 //internal string GetInformation(string pluginName) { 55 // return GetInformation(GetPluginDescription(pluginName)); 56 //} 57 58 //private string GetInformation(PluginDescription desc) { 59 // StringBuilder builder = new StringBuilder(); 60 // builder.Append("Name: ").AppendLine(desc.Name); 61 // builder.Append("Version: ").AppendLine(desc.Version.ToString()); 62 // builder.AppendLine("Description:").AppendLine(desc.Description); 63 // if (!string.IsNullOrEmpty(desc.ContactName)) { 64 // builder.Append("Contact: ").Append(desc.ContactName).Append(", ").AppendLine(desc.ContactEmail); 65 // } 66 // builder.AppendLine("This plugin is " + desc.PluginState.ToString().ToLowerInvariant() + "."); 67 // builder.AppendLine("Files: "); 68 // foreach (var file in desc.Files) { 69 // builder.AppendLine(file.Type + " " + file.Name); 70 // } 71 // builder.AppendLine().AppendLine("Directly depends on:"); 72 // if (desc.Dependencies.Count() == 0) builder.AppendLine("None"); 73 // foreach (var dependency in desc.Dependencies) { 74 // builder.AppendLine(dependency.Name + " " + dependency.Version); 75 // } 76 // builder.AppendLine().AppendFormat("Plugins directly dependent on {0}:", desc.Name).AppendLine(); 77 // var dependents = from x in pluginManager.Plugins 78 // where x.Dependencies.Contains(desc) 79 // select x; 80 // if (dependents.Count() == 0) builder.AppendLine("None"); 81 // foreach (var dependent in dependents) { 82 // builder.AppendLine(dependent.Name + " " + dependent.Version); 83 // } 84 // builder.AppendLine(); 85 // if (desc.PluginState == PluginState.Disabled) { 86 // builder.AppendLine(DetermineProblem(desc)); 87 // } 88 89 // return builder.ToString(); 90 //} 91 92 //private static string DetermineProblem(PluginDescription desc) { 93 // // either any file is missing 94 // StringBuilder builder = new StringBuilder(); 95 // builder.AppendLine("Problem report:"); 96 // builder.AppendLine(desc.LoadingErrorInformation); 97 // return builder.ToString(); 98 //} 99 100 //private PluginDescription GetPluginDescription(string pluginName) { 101 // var exactMatch = from pluginDesc in pluginManager.Plugins 159 102 // where string.Equals(pluginName, pluginDesc.Name, StringComparison.InvariantCultureIgnoreCase) 160 103 // select pluginDesc; 161 // var inexactMatch = from pluginDesc in plugin s104 // var inexactMatch = from pluginDesc in pluginManager.Plugins 162 105 // where MatchPluginNameInexact(pluginName, pluginDesc.Name) 163 106 // select pluginDesc; … … 165 108 //} 166 109 167 public void Remove(IEnumerable<string> pluginNames) { 168 var fileNames = from pluginToDelete in PluginDescriptionIterator.IterateDependentsTopDown(GetPluginDescriptions(pluginNames), pluginManager.Plugins) 110 //private IEnumerable<PluginDescription> GetPluginDescriptions(IEnumerable<string> pluginNames) { 111 // return from pluginName in pluginNames 112 // select GetPluginDescription(pluginName); 113 //} 114 115 //private static bool MatchPluginNameInexact(string similarName, string actualName) { 116 // return 117 // // Core-3.2 == HeuristicLab.Core-3.2 118 // actualName.Equals("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase) || 119 // // HeuristicLab.Core == HeuristicLab.Core-3.2 (this should be save because we checked for exact matches first) 120 // (Math.Abs(actualName.Length - similarName.Length) <= 4 && actualName.StartsWith(similarName, StringComparison.InvariantCultureIgnoreCase)) || 121 // // Core == HeuristicLab.Core-3.2 122 // (Math.Abs(actualName.Length - similarName.Length) <= 17 && actualName.StartsWith("HeuristicLab." + similarName, StringComparison.InvariantCultureIgnoreCase)); 123 //} 124 125 126 /// <summary> 127 /// Retrieves a list of plugins available at the remote server 128 /// </summary> 129 /// <param name="connectionString"></param> 130 /// <returns></returns> 131 public IEnumerable<IPluginDescription> GetRemotePluginList(string connectionString) { 132 using (var client = new DeploymentService.UpdateClient()) { 133 return client.GetPlugins(); 134 } 135 } 136 137 /// <summary> 138 /// Retrieves the list of products available at the remote server 139 /// </summary> 140 /// <param name="connectionString"></param> 141 /// <returns></returns> 142 public IEnumerable<DeploymentService.ProductDescription> GetRemoteProductList(string connectionString) { 143 using (var client = new DeploymentService.UpdateClient()) { 144 return client.GetProducts(); 145 } 146 } 147 148 /// <summary> 149 /// Installs plugins from remote server 150 /// </summary> 151 /// <param name="connectionString"></param> 152 /// <param name="pluginNames"></param> 153 public void Install(string connectionString, IEnumerable<IPluginDescription> plugins) { 154 using (var client = new DeploymentService.UpdateClient()) { 155 var args = new PluginInfrastructureCancelEventArgs(plugins.Select(x => x.Name + " " + x.Version)); 156 OnPreInstall(args); 157 foreach (DeploymentService.PluginDescription plugin in plugins) { 158 byte[] zippedPackage = client.GetPlugin(plugin); 159 Unpack(zippedPackage); 160 OnInstalled(new PluginInfrastructureEventArgs(plugin)); 161 } 162 } 163 } 164 165 /// <summary> 166 /// Updates plugins from remote server 167 /// </summary> 168 /// <param name="pluginNames"></param> 169 public void Update(string connectionString, IEnumerable<IPluginDescription> plugins) { 170 PluginInfrastructureCancelEventArgs args = new PluginInfrastructureCancelEventArgs(plugins.Select(x => x.Name + " " + x.Version)); 171 OnPreUpdate(args); 172 if (!args.Cancel) { 173 using (var client = new DeploymentService.UpdateClient()) { 174 foreach (DeploymentService.PluginDescription plugin in plugins) { 175 byte[] zippedPackage = client.GetPlugin(plugin); 176 Unpack(zippedPackage); 177 OnUpdated(new PluginInfrastructureEventArgs(plugin)); 178 } 179 } 180 } 181 } 182 183 /// <summary> 184 /// Deletes all plugin files from local installation 185 /// </summary> 186 /// <param name="pluginNames"></param> 187 public void Remove(IEnumerable<IPluginDescription> plugins) { 188 var fileNames = from pluginToDelete in plugins 169 189 from file in pluginToDelete.Files 170 190 select Path.Combine(pluginDir, file.Name); 171 var args = new PluginInfrastructureCancelEventArgs( "Deleting",fileNames);191 var args = new PluginInfrastructureCancelEventArgs(fileNames); 172 192 OnPreDelete(args); 173 193 if (!args.Cancel) { 174 194 foreach (string fileName in fileNames) { 175 Console.WriteLine("Deleting file " + fileName); 176 // File.Delete(fileName); 177 } 178 179 OnDeleted(new PluginInfrastructureEventArgs("Deleted", fileNames)); 180 } 181 } 182 183 public void Update(IEnumerable<string> pluginNames) { 184 var pluginDescriptions = from name in pluginNames 185 select GetPluginDescription(name); 186 Dictionary<DeploymentService.PluginDescription, string> matchingPlugins = new Dictionary<DeploymentService.PluginDescription, string>(); 187 foreach (var updateLocation in HeuristicLab.PluginInfrastructure.Properties.Settings.Default.UpdateLocations) { 188 using (var client = new DeploymentService.UpdateClient("", updateLocation)) { 189 var updateLocationMatchingPlugins = from desc in pluginDescriptions 190 from info in client.GetPlugins() 191 where desc.Name == info.Name 192 select info; 193 foreach (DeploymentService.PluginDescription info in updateLocationMatchingPlugins) { 194 // keep only the highest version of any plugin 195 var existingPlugin = matchingPlugins.Keys.FirstOrDefault(x => x.Name == info.Name); 196 if (existingPlugin == null || existingPlugin.Version < info.Version) { 197 matchingPlugins.Remove(existingPlugin); 198 matchingPlugins.Add(info, updateLocation); 195 File.Delete(fileName); 196 OnDeleted(new PluginInfrastructureEventArgs(fileName)); 197 } 198 } 199 } 200 201 private void Unpack(byte[] zippedPackage) { 202 using (ZipInputStream s = new ZipInputStream(new MemoryStream(zippedPackage))) { 203 ZipEntry theEntry; 204 string tmpEntry = String.Empty; 205 while ((theEntry = s.GetNextEntry()) != null) { 206 string directoryName = pluginDir; 207 string fileName = Path.GetFileName(theEntry.Name); 208 // create directory 209 if (directoryName != "") { 210 Directory.CreateDirectory(directoryName); 211 } 212 if (fileName != String.Empty) { 213 string fullPath = Path.Combine(directoryName, fileName); 214 string fullDirPath = Path.GetDirectoryName(fullPath); 215 if (!Directory.Exists(fullDirPath)) Directory.CreateDirectory(fullDirPath); 216 FileStream streamWriter = File.Create(fullPath); 217 int size = 2048; 218 byte[] data = new byte[2048]; 219 while (true) { 220 size = s.Read(data, 0, data.Length); 221 if (size > 0) { 222 streamWriter.Write(data, 0, size); 223 } else { 224 break; 225 } 199 226 } 227 streamWriter.Close(); 200 228 } 201 229 } 202 }203 PluginInfrastructureCancelEventArgs args = new PluginInfrastructureCancelEventArgs("Updating", matchingPlugins.Keys);204 OnPreUpdate(args);205 if (!args.Cancel) {206 var groupedInfos = matchingPlugins.GroupBy(x => x.Value);207 foreach (var group in groupedInfos) {208 using (var client = new DeploymentService.UpdateClient(group.Key)) {209 foreach (var info in group) {210 client.GetPlugin(info.Key);211 }212 }213 }214 OnUpdated(new PluginInfrastructureEventArgs("Updated", matchingPlugins.Keys));215 230 } 216 231 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/InstallationManagerConsole.cs
r2811 r2922 27 27 using System.IO; 28 28 using System.ComponentModel; 29 using HeuristicLab.PluginInfrastructure.Advanced.DeploymentService; 29 30 30 31 … … 32 33 public class InstallationManagerConsole { 33 34 private InstallationManager installManager; 35 private string connectionString; 34 36 public InstallationManagerConsole(string pluginDir) { 37 38 // get default connection string 39 using (var client = new UpdateClient()) { 40 connectionString = client.Endpoint.Address.ToString(); 41 } 42 35 43 this.installManager = new InstallationManager(pluginDir); 36 44 installManager.PreInstallPlugin += new EventHandler<PluginInfrastructureCancelEventArgs>(installManager_PreInstallPlugin); … … 44 52 void installManager_PreUpdatePlugin(object sender, PluginInfrastructureCancelEventArgs e) { 45 53 Console.WriteLine("Following plugins are updated:"); 46 var infos = (IEnumerable<PluginDescription>)e.Entity; 47 foreach (var info in infos) { 48 Console.WriteLine(info.Name + " " + info.Version); 54 foreach (var info in e.Entities) { 55 Console.WriteLine(e); 49 56 } 50 57 if (GetUserConfirmation()) e.Cancel = false; … … 54 61 55 62 void installManager_PluginUpdated(object sender, PluginInfrastructureEventArgs e) { 56 foreach (var info in (IEnumerable< PluginDescription>)e.Entity)63 foreach (var info in (IEnumerable<IPluginDescription>)e.Entity) 57 64 Console.WriteLine("Updated: {0}", info.Name); 58 65 } … … 60 67 void installManager_PreRemovePlugin(object sender, PluginInfrastructureCancelEventArgs e) { 61 68 Console.WriteLine("Following files are deleted:"); 62 var fileNames = (IEnumerable<string>)e.Entity; 63 foreach (string fileName in fileNames) { 69 foreach (string fileName in e.Entities) { 64 70 Console.WriteLine(fileName); 65 71 } … … 90 96 91 97 public void Show(IEnumerable<string> pluginNames) { 92 foreach (string pluginName in pluginNames)93 Console.WriteLine(installManager.GetInformation(pluginName));98 //foreach (string pluginName in pluginNames) 99 // Console.WriteLine(installManager.GetInformation(pluginName)); 94 100 } 95 101 96 102 public void Install(IEnumerable<string> pluginNames) { 97 installManager.Install(pluginNames);103 //installManager.Install(connectionString, pluginNames); 98 104 } 99 105 100 106 public void Remove(IEnumerable<string> pluginNames) { 101 installManager.Remove(pluginNames);107 // installManager.Remove(pluginNames); 102 108 } 103 109 104 110 public void Update(IEnumerable<string> pluginNames) { 105 installManager.Update(pluginNames);111 // installManager.Update(connectionString, pluginNames); 106 112 } 107 113 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/InstallationManagerForm.Designer.cs
r2753 r2922 25 25 private void InitializeComponent() { 26 26 this.statusStrip = new System.Windows.Forms.StatusStrip(); 27 this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); 28 this.detailsTextBox = new System.Windows.Forms.TextBox(); 29 this.pluginsListView = new System.Windows.Forms.ListView(); 30 this.tableLayoutPanel.SuspendLayout(); 27 this.toolStripProgressBar = new System.Windows.Forms.ToolStripProgressBar(); 28 this.toolStripStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); 29 this.removeButton = new System.Windows.Forms.Button(); 30 this.serverUrlLabel = new System.Windows.Forms.Label(); 31 this.serverUrlTextBox = new System.Windows.Forms.TextBox(); 32 this.refreshButton = new System.Windows.Forms.Button(); 33 this.installButton = new System.Windows.Forms.Button(); 34 this.tabControl = new System.Windows.Forms.TabControl(); 35 this.localPluginsTabPage = new System.Windows.Forms.TabPage(); 36 this.localPluginManager = new HeuristicLab.PluginInfrastructure.Advanced.LocalPluginManager(); 37 this.remotePluginsTabPage = new System.Windows.Forms.TabPage(); 38 this.remotePluginInstaller = new HeuristicLab.PluginInfrastructure.Advanced.RemotePluginInstaller(); 39 this.logTabPage = new System.Windows.Forms.TabPage(); 40 this.logTextBox = new System.Windows.Forms.TextBox(); 41 this.statusStrip.SuspendLayout(); 42 this.tabControl.SuspendLayout(); 43 this.localPluginsTabPage.SuspendLayout(); 44 this.remotePluginsTabPage.SuspendLayout(); 45 this.logTabPage.SuspendLayout(); 31 46 this.SuspendLayout(); 32 47 // 33 48 // statusStrip 34 49 // 35 this.statusStrip.Location = new System.Drawing.Point(0, 433); 50 this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 51 this.toolStripProgressBar, 52 this.toolStripStatusLabel}); 53 this.statusStrip.Location = new System.Drawing.Point(0, 612); 36 54 this.statusStrip.Name = "statusStrip"; 37 this.statusStrip.Size = new System.Drawing.Size( 394, 22);55 this.statusStrip.Size = new System.Drawing.Size(606, 22); 38 56 this.statusStrip.TabIndex = 0; 39 this.statusStrip.Text = "statusStrip1"; 40 // 41 // tableLayoutPanel 42 // 43 this.tableLayoutPanel.ColumnCount = 1; 44 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); 45 this.tableLayoutPanel.Controls.Add(this.detailsTextBox, 0, 1); 46 this.tableLayoutPanel.Controls.Add(this.pluginsListView, 0, 0); 47 this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; 48 this.tableLayoutPanel.Location = new System.Drawing.Point(0, 0); 49 this.tableLayoutPanel.Name = "tableLayoutPanel"; 50 this.tableLayoutPanel.RowCount = 2; 51 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 64F)); 52 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 36F)); 53 this.tableLayoutPanel.Size = new System.Drawing.Size(394, 433); 54 this.tableLayoutPanel.TabIndex = 1; 55 // 56 // detailsTextBox 57 // 58 this.detailsTextBox.Dock = System.Windows.Forms.DockStyle.Fill; 59 this.detailsTextBox.Location = new System.Drawing.Point(3, 280); 60 this.detailsTextBox.Multiline = true; 61 this.detailsTextBox.Name = "detailsTextBox"; 62 this.detailsTextBox.ReadOnly = true; 63 this.detailsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 64 this.detailsTextBox.Size = new System.Drawing.Size(388, 150); 65 this.detailsTextBox.TabIndex = 0; 66 // 67 // pluginsListView 68 // 69 this.pluginsListView.Dock = System.Windows.Forms.DockStyle.Fill; 70 this.pluginsListView.Location = new System.Drawing.Point(3, 3); 71 this.pluginsListView.MultiSelect = false; 72 this.pluginsListView.Name = "pluginsListView"; 73 this.pluginsListView.Size = new System.Drawing.Size(388, 271); 74 this.pluginsListView.TabIndex = 1; 75 this.pluginsListView.UseCompatibleStateImageBehavior = false; 76 this.pluginsListView.View = System.Windows.Forms.View.List; 77 this.pluginsListView.SelectedIndexChanged += new System.EventHandler(this.pluginsListView_SelectedIndexChanged); 57 // 58 // toolStripProgressBar 59 // 60 this.toolStripProgressBar.MarqueeAnimationSpeed = 30; 61 this.toolStripProgressBar.Name = "toolStripProgressBar"; 62 this.toolStripProgressBar.Size = new System.Drawing.Size(100, 16); 63 this.toolStripProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 64 this.toolStripProgressBar.Visible = false; 65 // 66 // toolStripStatusLabel 67 // 68 this.toolStripStatusLabel.Name = "toolStripStatusLabel"; 69 this.toolStripStatusLabel.Size = new System.Drawing.Size(0, 17); 70 // 71 // removeButton 72 // 73 this.removeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 74 this.removeButton.Enabled = false; 75 this.removeButton.Location = new System.Drawing.Point(6, 557); 76 this.removeButton.Name = "removeButton"; 77 this.removeButton.Size = new System.Drawing.Size(109, 23); 78 this.removeButton.TabIndex = 11; 79 this.removeButton.Text = "Remove Plugins"; 80 this.removeButton.UseVisualStyleBackColor = true; 81 this.removeButton.Click += new System.EventHandler(this.removeButton_Click); 82 // 83 // serverUrlLabel 84 // 85 this.serverUrlLabel.AutoSize = true; 86 this.serverUrlLabel.Location = new System.Drawing.Point(9, 11); 87 this.serverUrlLabel.Name = "serverUrlLabel"; 88 this.serverUrlLabel.Size = new System.Drawing.Size(73, 13); 89 this.serverUrlLabel.TabIndex = 13; 90 this.serverUrlLabel.Text = "Plugin Server:"; 91 // 92 // serverUrlTextBox 93 // 94 this.serverUrlTextBox.Location = new System.Drawing.Point(88, 8); 95 this.serverUrlTextBox.Name = "serverUrlTextBox"; 96 this.serverUrlTextBox.Size = new System.Drawing.Size(264, 20); 97 this.serverUrlTextBox.TabIndex = 12; 98 // 99 // refreshButton 100 // 101 this.refreshButton.Location = new System.Drawing.Point(358, 6); 102 this.refreshButton.Name = "refreshButton"; 103 this.refreshButton.Size = new System.Drawing.Size(75, 23); 104 this.refreshButton.TabIndex = 11; 105 this.refreshButton.Text = "Refresh"; 106 this.refreshButton.UseVisualStyleBackColor = true; 107 this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click); 108 // 109 // installButton 110 // 111 this.installButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 112 this.installButton.Enabled = false; 113 this.installButton.Location = new System.Drawing.Point(6, 557); 114 this.installButton.Name = "installButton"; 115 this.installButton.Size = new System.Drawing.Size(132, 23); 116 this.installButton.TabIndex = 15; 117 this.installButton.Text = "Download and Install"; 118 this.installButton.UseVisualStyleBackColor = true; 119 this.installButton.Click += new System.EventHandler(this.updateButton_Click); 120 // 121 // tabControl 122 // 123 this.tabControl.Controls.Add(this.localPluginsTabPage); 124 this.tabControl.Controls.Add(this.remotePluginsTabPage); 125 this.tabControl.Controls.Add(this.logTabPage); 126 this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; 127 this.tabControl.Location = new System.Drawing.Point(0, 0); 128 this.tabControl.Name = "tabControl"; 129 this.tabControl.SelectedIndex = 0; 130 this.tabControl.Size = new System.Drawing.Size(606, 612); 131 this.tabControl.TabIndex = 16; 132 // 133 // localPluginsTabPage 134 // 135 this.localPluginsTabPage.Controls.Add(this.removeButton); 136 this.localPluginsTabPage.Controls.Add(this.localPluginManager); 137 this.localPluginsTabPage.Location = new System.Drawing.Point(4, 22); 138 this.localPluginsTabPage.Name = "localPluginsTabPage"; 139 this.localPluginsTabPage.Padding = new System.Windows.Forms.Padding(3); 140 this.localPluginsTabPage.Size = new System.Drawing.Size(598, 586); 141 this.localPluginsTabPage.TabIndex = 0; 142 this.localPluginsTabPage.Text = "Installed Plugins"; 143 this.localPluginsTabPage.UseVisualStyleBackColor = true; 144 // 145 // localPluginManager 146 // 147 this.localPluginManager.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 148 | System.Windows.Forms.AnchorStyles.Left) 149 | System.Windows.Forms.AnchorStyles.Right))); 150 this.localPluginManager.Location = new System.Drawing.Point(6, 6); 151 this.localPluginManager.Name = "localPluginManager"; 152 this.localPluginManager.Plugins = null; 153 this.localPluginManager.Size = new System.Drawing.Size(584, 545); 154 this.localPluginManager.TabIndex = 0; 155 this.localPluginManager.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.localPluginManager_ItemChecked); 156 // 157 // remotePluginsTabPage 158 // 159 this.remotePluginsTabPage.Controls.Add(this.serverUrlLabel); 160 this.remotePluginsTabPage.Controls.Add(this.remotePluginInstaller); 161 this.remotePluginsTabPage.Controls.Add(this.serverUrlTextBox); 162 this.remotePluginsTabPage.Controls.Add(this.refreshButton); 163 this.remotePluginsTabPage.Controls.Add(this.installButton); 164 this.remotePluginsTabPage.Location = new System.Drawing.Point(4, 22); 165 this.remotePluginsTabPage.Name = "remotePluginsTabPage"; 166 this.remotePluginsTabPage.Padding = new System.Windows.Forms.Padding(3); 167 this.remotePluginsTabPage.Size = new System.Drawing.Size(598, 586); 168 this.remotePluginsTabPage.TabIndex = 1; 169 this.remotePluginsTabPage.Text = "Remote Plugins"; 170 this.remotePluginsTabPage.UseVisualStyleBackColor = true; 171 // 172 // remotePluginInstaller 173 // 174 this.remotePluginInstaller.AllPlugins = new HeuristicLab.PluginInfrastructure.IPluginDescription[0]; 175 this.remotePluginInstaller.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 176 | System.Windows.Forms.AnchorStyles.Left) 177 | System.Windows.Forms.AnchorStyles.Right))); 178 this.remotePluginInstaller.Location = new System.Drawing.Point(6, 35); 179 this.remotePluginInstaller.Name = "remotePluginInstaller"; 180 this.remotePluginInstaller.NewPlugins = new HeuristicLab.PluginInfrastructure.IPluginDescription[0]; 181 this.remotePluginInstaller.Products = new HeuristicLab.PluginInfrastructure.Advanced.DeploymentService.ProductDescription[0]; 182 this.remotePluginInstaller.Size = new System.Drawing.Size(584, 516); 183 this.remotePluginInstaller.TabIndex = 14; 184 this.remotePluginInstaller.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.remotePluginInstaller_ItemChecked); 185 // 186 // logTabPage 187 // 188 this.logTabPage.Controls.Add(this.logTextBox); 189 this.logTabPage.Location = new System.Drawing.Point(4, 22); 190 this.logTabPage.Name = "logTabPage"; 191 this.logTabPage.Size = new System.Drawing.Size(598, 586); 192 this.logTabPage.TabIndex = 2; 193 this.logTabPage.Text = "Log"; 194 this.logTabPage.UseVisualStyleBackColor = true; 195 // 196 // logTextBox 197 // 198 this.logTextBox.Dock = System.Windows.Forms.DockStyle.Fill; 199 this.logTextBox.Location = new System.Drawing.Point(0, 0); 200 this.logTextBox.Multiline = true; 201 this.logTextBox.Name = "logTextBox"; 202 this.logTextBox.ReadOnly = true; 203 this.logTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 204 this.logTextBox.Size = new System.Drawing.Size(598, 586); 205 this.logTextBox.TabIndex = 0; 78 206 // 79 207 // InstallationManagerForm … … 81 209 this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 82 210 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 83 this.ClientSize = new System.Drawing.Size( 394, 455);84 this.Controls.Add(this.tab leLayoutPanel);211 this.ClientSize = new System.Drawing.Size(606, 634); 212 this.Controls.Add(this.tabControl); 85 213 this.Controls.Add(this.statusStrip); 86 214 this.Name = "InstallationManagerForm"; 87 215 this.Text = "InstallationManager"; 88 this.tableLayoutPanel.ResumeLayout(false); 89 this.tableLayoutPanel.PerformLayout(); 216 this.statusStrip.ResumeLayout(false); 217 this.statusStrip.PerformLayout(); 218 this.tabControl.ResumeLayout(false); 219 this.localPluginsTabPage.ResumeLayout(false); 220 this.remotePluginsTabPage.ResumeLayout(false); 221 this.remotePluginsTabPage.PerformLayout(); 222 this.logTabPage.ResumeLayout(false); 223 this.logTabPage.PerformLayout(); 90 224 this.ResumeLayout(false); 91 225 this.PerformLayout(); … … 96 230 97 231 private System.Windows.Forms.StatusStrip statusStrip; 98 private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; 99 private System.Windows.Forms.TextBox detailsTextBox; 100 private System.Windows.Forms.ListView pluginsListView; 232 private System.Windows.Forms.ToolStripProgressBar toolStripProgressBar; 233 private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel; 234 private System.Windows.Forms.Label serverUrlLabel; 235 private System.Windows.Forms.TextBox serverUrlTextBox; 236 private System.Windows.Forms.Button refreshButton; 237 private LocalPluginManager localPluginManager; 238 private RemotePluginInstaller remotePluginInstaller; 239 private System.Windows.Forms.Button removeButton; 240 private System.Windows.Forms.Button installButton; 241 private System.Windows.Forms.TabControl tabControl; 242 private System.Windows.Forms.TabPage localPluginsTabPage; 243 private System.Windows.Forms.TabPage remotePluginsTabPage; 244 private System.Windows.Forms.TabPage logTabPage; 245 private System.Windows.Forms.TextBox logTextBox; 101 246 } 102 247 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Advanced/InstallationManagerForm.cs
r2753 r2922 12 12 namespace HeuristicLab.PluginInfrastructure.Advanced { 13 13 public partial class InstallationManagerForm : Form { 14 private class UpdateOrInstallPluginsBackgroundWorkerArgument { 15 public string ConnectionString { get; set; } 16 public IEnumerable<IPluginDescription> PluginsToUpdate { get; set; } 17 public IEnumerable<IPluginDescription> PluginsToInstall { get; set; } 18 } 19 20 private class RemovePluginsBackgroundWorkerArgument { 21 public IEnumerable<IPluginDescription> PluginsToRemove { get; set; } 22 } 23 24 private class RefreshBackgroundWorkerResult { 25 public IEnumerable<IPluginDescription> RemotePlugins { get; set; } 26 public IEnumerable<DeploymentService.ProductDescription> RemoteProducts { get; set; } 27 } 28 14 29 private InstallationManager installationManager; 30 private BackgroundWorker refreshServerPluginsBackgroundWorker; 31 private BackgroundWorker updateOrInstallPluginsBackgroundWorker; 32 private BackgroundWorker removePluginsBackgroundWorker; 33 private BackgroundWorker refreshLocalPluginsBackgroundWorker; 34 private string pluginDir; 15 35 16 36 public InstallationManagerForm() { 17 37 InitializeComponent(); 18 this.installationManager = new InstallationManager(Path.GetDirectoryName(Application.ExecutablePath)); 19 20 UpdatePluginsList(); 21 } 22 23 private void UpdatePluginsList() { 24 foreach (var plugin in installationManager.Plugins) { 25 pluginsListView.Items.Add(CreatePluginItem(plugin)); 26 } 27 } 28 29 private static ListViewItem CreatePluginItem(IPluginDescription plugin) { 30 ListViewItem item = new ListViewItem(); 31 item.Tag = plugin; 32 item.Text = plugin.Name + "-" + plugin.Version.ToString(); 33 return item; 34 } 35 36 private void pluginsListView_SelectedIndexChanged(object sender, EventArgs e) { 37 if (pluginsListView.SelectedItems.Count > 0) { 38 ListViewItem selecteditem = pluginsListView.SelectedItems[0]; 39 IPluginDescription desc = (IPluginDescription)selecteditem.Tag; 40 UpdateDetailsBox((PluginDescription)desc); 41 } 42 } 43 44 private void UpdateDetailsBox(PluginDescription desc) { 45 detailsTextBox.Text = installationManager.GetInformation(desc.Name); 38 39 pluginDir = Application.StartupPath; 40 41 #region initialize background workers 42 refreshServerPluginsBackgroundWorker = new BackgroundWorker(); 43 refreshServerPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshServerPluginsBackgroundWorker_DoWork); 44 refreshServerPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshServerPluginsBackgroundWorker_RunWorkerCompleted); 45 46 updateOrInstallPluginsBackgroundWorker = new BackgroundWorker(); 47 updateOrInstallPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(updateOrInstallPluginsBackgroundWorker_DoWork); 48 updateOrInstallPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted); 49 50 removePluginsBackgroundWorker = new BackgroundWorker(); 51 removePluginsBackgroundWorker.DoWork += new DoWorkEventHandler(removePluginsBackgroundWorker_DoWork); 52 removePluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(removePluginsBackgroundWorker_RunWorkerCompleted); 53 54 refreshLocalPluginsBackgroundWorker = new BackgroundWorker(); 55 refreshLocalPluginsBackgroundWorker.DoWork += new DoWorkEventHandler(refreshLocalPluginsBackgroundWorker_DoWork); 56 refreshLocalPluginsBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(refreshLocalPluginsBackgroundWorker_RunWorkerCompleted); 57 #endregion 58 59 // get default connection string 60 using (var client = new DeploymentService.UpdateClient()) { 61 serverUrlTextBox.Text = client.Endpoint.Address.ToString(); 62 } 63 64 installationManager = new InstallationManager(pluginDir); 65 installationManager.PluginInstalled += new EventHandler<PluginInfrastructureEventArgs>(installationManager_PluginInstalled); 66 installationManager.PluginRemoved += new EventHandler<PluginInfrastructureEventArgs>(installationManager_PluginRemoved); 67 installationManager.PluginUpdated += new EventHandler<PluginInfrastructureEventArgs>(installationManager_PluginUpdated); 68 installationManager.PreInstallPlugin += new EventHandler<PluginInfrastructureCancelEventArgs>(installationManager_PreInstallPlugin); 69 installationManager.PreRemovePlugin += new EventHandler<PluginInfrastructureCancelEventArgs>(installationManager_PreRemovePlugin); 70 installationManager.PreUpdatePlugin += new EventHandler<PluginInfrastructureCancelEventArgs>(installationManager_PreUpdatePlugin); 71 72 RefreshLocalPluginListAsync(); 73 } 74 75 #region event handlers for refresh local plugin list backgroundworker 76 private IEnumerable<PluginDescription> ReloadLocalPlugins() { 77 PluginManager pluginManager = new PluginManager(Application.StartupPath); 78 pluginManager.PluginLoaded += pluginManager_PluginLoaded; 79 pluginManager.PluginUnloaded += pluginManager_PluginUnloaded; 80 pluginManager.Initializing += pluginManager_Initializing; 81 pluginManager.Initialized += pluginManager_Initialized; 82 83 pluginManager.DiscoverAndCheckPlugins(); 84 85 pluginManager.PluginLoaded -= pluginManager_PluginLoaded; 86 pluginManager.PluginUnloaded -= pluginManager_PluginUnloaded; 87 pluginManager.Initializing -= pluginManager_Initializing; 88 pluginManager.Initialized -= pluginManager_Initialized; 89 90 return pluginManager.Plugins; 91 } 92 void refreshLocalPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 93 if (!e.Cancelled && e.Error == null) { 94 UpdateLocalPluginList((IEnumerable<PluginDescription>)e.Result); 95 UpdateControlsConnected(); 96 } 97 } 98 99 void refreshLocalPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { 100 var plugins = ReloadLocalPlugins(); 101 e.Result = plugins; 102 } 103 #endregion 104 105 #region event handlers for plugin removal background worker 106 void removePluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 107 if (!e.Cancelled && e.Error == null) { 108 RefreshLocalPluginListAsync(); 109 UpdateControlsConnected(); 110 } 111 } 112 113 void removePluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { 114 IEnumerable<IPluginDescription> pluginsToRemove = (IEnumerable<IPluginDescription>)e.Argument; 115 installationManager.Remove(pluginsToRemove); 116 } 117 #endregion 118 119 #region event handlers for plugin update background worker 120 void updateOrInstallPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 121 if (!e.Cancelled && e.Error == null) { 122 RefreshLocalPluginListAsync(); 123 RefreshRemotePluginListAsync(); 124 UpdateControlsConnected(); 125 } else { 126 UpdateControlsDisconnected(); 127 } 128 } 129 130 void updateOrInstallPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { 131 UpdateOrInstallPluginsBackgroundWorkerArgument info = (UpdateOrInstallPluginsBackgroundWorkerArgument)e.Argument; 132 installationManager.Install(info.ConnectionString, info.PluginsToInstall); 133 installationManager.Update(info.ConnectionString, info.PluginsToUpdate); 134 } 135 #endregion 136 137 #region event handlers for refresh server plugins background worker 138 void refreshServerPluginsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 139 if (!e.Cancelled && e.Result != null) { 140 RefreshBackgroundWorkerResult refreshResult = (RefreshBackgroundWorkerResult)e.Result; 141 UpdateRemotePluginList(refreshResult.RemoteProducts, refreshResult.RemotePlugins); 142 UpdateControlsConnected(); 143 } else { 144 UpdateControlsDisconnected(); 145 } 146 } 147 148 void refreshServerPluginsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) { 149 string connectionString = (string)e.Argument; 150 151 RefreshBackgroundWorkerResult result = new RefreshBackgroundWorkerResult(); 152 result.RemotePlugins = installationManager.GetRemotePluginList(connectionString); 153 result.RemoteProducts = installationManager.GetRemoteProductList(connectionString); 154 e.Cancel = false; 155 e.Result = result; 156 } 157 158 159 160 #endregion 161 162 #region plugin manager event handlers 163 void pluginManager_Initialized(object sender, PluginInfrastructureEventArgs e) { 164 SetStatusStrip("Initialized PluginInfrastructure"); 165 } 166 167 void pluginManager_Initializing(object sender, PluginInfrastructureEventArgs e) { 168 SetStatusStrip("Initializing PluginInfrastructure"); 169 } 170 171 void pluginManager_PluginUnloaded(object sender, PluginInfrastructureEventArgs e) { 172 SetStatusStrip("Unloaded " + e.Entity); 173 } 174 175 void pluginManager_PluginLoaded(object sender, PluginInfrastructureEventArgs e) { 176 SetStatusStrip("Loaded " + e.Entity); 177 } 178 #endregion 179 180 #region installation manager event handlers 181 void installationManager_PreUpdatePlugin(object sender, PluginInfrastructureCancelEventArgs e) { 182 if (ConfirmUpdateAction(e.Entities) == true) e.Cancel = false; 183 else e.Cancel = true; 184 } 185 186 void installationManager_PreRemovePlugin(object sender, PluginInfrastructureCancelEventArgs e) { 187 if (ConfirmRemoveAction(e.Entities) == true) e.Cancel = false; 188 else e.Cancel = true; 189 } 190 191 void installationManager_PreInstallPlugin(object sender, PluginInfrastructureCancelEventArgs e) { 192 if (e.Entities.Count() > 0) 193 SetStatusStrip("Installing " + e.Entities.Aggregate((a, b) => a + Environment.NewLine + b)); 194 } 195 196 void installationManager_PluginUpdated(object sender, PluginInfrastructureEventArgs e) { 197 SetStatusStrip("Updated " + e.Entity); 198 } 199 200 void installationManager_PluginRemoved(object sender, PluginInfrastructureEventArgs e) { 201 SetStatusStrip("Removed " + e.Entity); 202 } 203 204 void installationManager_PluginInstalled(object sender, PluginInfrastructureEventArgs e) { 205 SetStatusStrip("Installed " + e.Entity); 206 } 207 #endregion 208 209 private void SetStatusStrip(string msg) { 210 if (InvokeRequired) Invoke((Action<string>)SetStatusStrip, msg); 211 else { 212 toolStripStatusLabel.Text = msg; 213 logTextBox.Text += DateTime.Now + ": " + msg + Environment.NewLine; 214 } 215 } 216 217 #region button events 218 219 private void refreshButton_Click(object sender, EventArgs e) { 220 RefreshRemotePluginListAsync(); 221 } 222 223 private void updateButton_Click(object sender, EventArgs e) { 224 Cursor = Cursors.AppStarting; 225 toolStripProgressBar.Visible = true; 226 DisableControls(); 227 var updateOrInstallInfo = new UpdateOrInstallPluginsBackgroundWorkerArgument(); 228 // if there is a local plugin with same name and same major and minor version then it's an update 229 var pluginsToUpdate = from remotePlugin in remotePluginInstaller.CheckedPlugins 230 let matchingLocalPlugins = from localPlugin in localPluginManager.Plugins 231 where localPlugin.Name == remotePlugin.Name 232 where localPlugin.Version.Major == remotePlugin.Version.Major 233 where localPlugin.Version.Minor == remotePlugin.Version.Minor 234 select localPlugin 235 where matchingLocalPlugins.Count() > 0 236 select remotePlugin; 237 238 // otherwise install a new plugin 239 var pluginsToInstall = remotePluginInstaller.CheckedPlugins.Except(pluginsToUpdate); 240 241 updateOrInstallInfo.ConnectionString = serverUrlTextBox.Text; 242 updateOrInstallInfo.PluginsToInstall = pluginsToInstall; 243 updateOrInstallInfo.PluginsToUpdate = pluginsToUpdate; 244 updateOrInstallPluginsBackgroundWorker.RunWorkerAsync(updateOrInstallInfo); 245 } 246 247 private void removeButton_Click(object sender, EventArgs e) { 248 Cursor = Cursors.AppStarting; 249 toolStripProgressBar.Visible = true; 250 DisableControls(); 251 removePluginsBackgroundWorker.RunWorkerAsync(localPluginManager.CheckedPlugins); 252 } 253 254 #endregion 255 256 #region confirmation dialogs 257 private bool ConfirmRemoveAction(IEnumerable<string> fileNames) { 258 StringBuilder strBuilder = new StringBuilder(); 259 strBuilder.AppendLine("Delete files:"); 260 foreach (var fileName in fileNames) { 261 strBuilder.AppendLine(fileName); 262 } 263 return MessageBox.Show(strBuilder.ToString(), "Confirm Delete", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK; 264 } 265 266 private bool ConfirmUpdateAction(IEnumerable<string> plugins) { 267 StringBuilder strBuilder = new StringBuilder(); 268 strBuilder.AppendLine("Update plugins:"); 269 foreach (var plugin in plugins) { 270 strBuilder.AppendLine(plugin); 271 } 272 return MessageBox.Show(strBuilder.ToString(), "Confirm Update", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK; 273 } 274 275 #endregion 276 277 #region helper methods 278 279 private void UpdateLocalPluginList(IEnumerable<PluginDescription> plugins) { 280 localPluginManager.Plugins = plugins; 281 } 282 283 private void UpdateRemotePluginList( 284 IEnumerable<DeploymentService.ProductDescription> remoteProducts, 285 IEnumerable<IPluginDescription> remotePlugins) { 286 287 var mostRecentRemotePlugins = from remote in remotePlugins 288 where !remotePlugins.Any(x => x.Name == remote.Name && x.Version > remote.Version) // same name and higher version 289 select remote; 290 291 var newPlugins = from remote in mostRecentRemotePlugins 292 let matchingLocal = (from local in localPluginManager.Plugins 293 where local.Name == remote.Name 294 where local.Version < remote.Version 295 select local).FirstOrDefault() 296 where matchingLocal != null 297 select remote; 298 299 remotePluginInstaller.NewPlugins = newPlugins; 300 remotePluginInstaller.Products = remoteProducts; 301 remotePluginInstaller.AllPlugins = remotePlugins; 302 } 303 304 private void RefreshRemotePluginListAsync() { 305 Cursor = Cursors.AppStarting; 306 toolStripProgressBar.Visible = true; 307 DisableControls(); 308 refreshServerPluginsBackgroundWorker.RunWorkerAsync(serverUrlTextBox.Text); 309 } 310 311 private void RefreshLocalPluginListAsync() { 312 Cursor = Cursors.AppStarting; 313 toolStripProgressBar.Visible = true; 314 DisableControls(); 315 refreshLocalPluginsBackgroundWorker.RunWorkerAsync(); 316 } 317 318 private void UpdateControlsDisconnected() { 319 //localPluginsListView.Enabled = false; 320 //ClearPluginsList(remotePluginsListView); 321 refreshButton.Enabled = true; 322 serverUrlTextBox.Enabled = true; 323 toolStripProgressBar.Visible = false; 324 Cursor = Cursors.Default; 325 } 326 327 private void UpdateControlsConnected() { 328 refreshButton.Enabled = true; 329 serverUrlTextBox.Enabled = true; 330 toolStripProgressBar.Visible = false; 331 Cursor = Cursors.Default; 332 } 333 334 private void DisableControls() { 335 refreshButton.Enabled = false; 336 Cursor = Cursors.Default; 337 } 338 #endregion 339 340 private void localPluginManager_ItemChecked(object sender, ItemCheckedEventArgs e) { 341 removeButton.Enabled = localPluginManager.CheckedPlugins.Count() > 0; 342 } 343 344 private void remotePluginInstaller_ItemChecked(object sender, ItemCheckedEventArgs e) { 345 installButton.Enabled = remotePluginInstaller.CheckedPlugins.Count() > 0; 46 346 } 47 347 } -
trunk/sources/HeuristicLab.PluginInfrastructure/ApplicationManager.cs
r2790 r2922 127 127 } 128 128 } 129 OnPluginLoaded(new PluginInfrastructureEventArgs( "Plugin loaded",desc));129 OnPluginLoaded(new PluginInfrastructureEventArgs(desc)); 130 130 desc.Load(); 131 131 } … … 149 149 foreach (var desc in PluginDescriptionIterator.IterateDependenciesBottomUp(plugins.Where(x => x.PluginState != PluginState.Disabled))) { 150 150 desc.Unload(); 151 OnPluginUnloaded(new PluginInfrastructureEventArgs( "Plugin unloaded",desc));151 OnPluginUnloaded(new PluginInfrastructureEventArgs(desc)); 152 152 } 153 153 } -
trunk/sources/HeuristicLab.PluginInfrastructure/HeuristicLab.PluginInfrastructure.csproj
r2900 r2922 75 75 </PropertyGroup> 76 76 <ItemGroup> 77 <Reference Include="ICSharpCode.SharpZipLib, Version=0.85.4.369, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL" /> 77 78 <Reference Include="System" /> 78 79 <Reference Include="System.Core"> … … 106 107 <Compile Include="Advanced\DeploymentService\ProductDescription.cs"> 107 108 <SubType>Code</SubType> 109 </Compile> 110 <Compile Include="Advanced\LocalPluginManager.cs"> 111 <SubType>UserControl</SubType> 112 </Compile> 113 <Compile Include="Advanced\LocalPluginManager.Designer.cs"> 114 <DependentUpon>LocalPluginManager.cs</DependentUpon> 115 </Compile> 116 <Compile Include="Advanced\RemotePluginInstaller.cs"> 117 <SubType>UserControl</SubType> 118 </Compile> 119 <Compile Include="Advanced\RemotePluginInstaller.Designer.cs"> 120 <DependentUpon>RemotePluginInstaller.cs</DependentUpon> 108 121 </Compile> 109 122 <Compile Include="Attributes\ApplicationAttribute.cs" /> … … 139 152 <DependentUpon>InstallationManagerForm.cs</DependentUpon> 140 153 </EmbeddedResource> 154 <EmbeddedResource Include="Advanced\LocalPluginManager.resx"> 155 <DependentUpon>LocalPluginManager.cs</DependentUpon> 156 </EmbeddedResource> 157 <EmbeddedResource Include="Advanced\RemotePluginInstaller.resx"> 158 <DependentUpon>RemotePluginInstaller.cs</DependentUpon> 159 </EmbeddedResource> 141 160 <EmbeddedResource Include="Properties\Resources.resx"> 142 161 <Generator>ResXFileCodeGenerator</Generator> 143 162 <LastGenOutput>Resources.Designer.cs</LastGenOutput> 144 163 <SubType>Designer</SubType> 164 </EmbeddedResource> 165 <EmbeddedResource Include="Resources\Resources.resx"> 166 <Generator>ResXFileCodeGenerator</Generator> 167 <LastGenOutput>Resources.Designer.cs</LastGenOutput> 145 168 </EmbeddedResource> 146 169 <EmbeddedResource Include="Starter\SplashScreen.resx"> … … 167 190 <DesignTimeSharedInput>True</DesignTimeSharedInput> 168 191 </Compile> 192 <Compile Include="Resources\Resources.Designer.cs"> 193 <AutoGen>True</AutoGen> 194 <DesignTime>True</DesignTime> 195 <DependentUpon>Resources.resx</DependentUpon> 196 </Compile> 169 197 <Compile Include="Sandboxing\SandboxManager.cs" /> 170 198 <Compile Include="Starter\SplashScreen.cs"> … … 193 221 <Content Include="Resources\List.gif" /> 194 222 <Content Include="Resources\Logo_white.gif" /> 223 <None Include="Resources\VS2008ImageLibrary_Actions_Delete.png" /> 224 <None Include="Resources\VS2008ImageLibrary_CommonElements_Actions_Remove.png" /> 225 <Content Include="Resources\VS2008ImageLibrary_CommonElements_Objects_Arrow_Down.png" /> 226 <None Include="Resources\VS2008ImageLibrary_Objects_Internet.png" /> 227 <None Include="Resources\VS2008ImageLibrary_Objects_Install.png" /> 228 <None Include="Resources\VS2008ImageLibrary_Objects_Assembly.png" /> 195 229 <None Include="Advanced\DeploymentService\RegenerateServiceClasses.cmd" /> 196 230 </ItemGroup> -
trunk/sources/HeuristicLab.PluginInfrastructure/Interfaces/IPluginDescription.cs
r2815 r2922 37 37 Version Version { get; } 38 38 /// <summary> 39 /// Gets the description of the plugin. 40 /// </summary> 41 string Description { get; } 42 /// <summary> 39 43 /// Gets the build date of the plugin. 40 44 /// </summary> -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/ApplicationDescription.cs
r2790 r2922 86 86 87 87 public override string ToString() { 88 return Name ;88 return Name + " " + Version; 89 89 } 90 90 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/PluginDescription.cs
r2815 r2922 48 48 /// Gets or sets the description of the plugin. 49 49 /// </summary> 50 internalstring Description {50 public string Description { 51 51 get { return description; } 52 set { description = value; }52 internal set { description = value; } 53 53 } 54 54 private Version version; … … 188 188 /// <returns>The name of the plugin.</returns> 189 189 public override string ToString() { 190 return Name ;190 return Name + " " + Version; 191 191 } 192 192 -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/PluginInfrastructureCancelEventArgs.cs
r2790 r2922 29 29 [Serializable] 30 30 internal sealed class PluginInfrastructureCancelEventArgs : CancelEventArgs { 31 internal string Action { get; private set; } 32 internal object Entity { get; private set; } 33 internal PluginInfrastructureCancelEventArgs(string action, object entity) 31 internal IEnumerable<string> Entities { get; private set; } 32 internal PluginInfrastructureCancelEventArgs(IEnumerable<string> entities) 34 33 : base() { 35 this.Action = action; 36 this.Entity = entity; 34 this.Entities = entities; 37 35 } 38 36 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/PluginInfrastructureEventArgs.cs
r2790 r2922 28 28 [Serializable] 29 29 internal sealed class PluginInfrastructureEventArgs : EventArgs { 30 internal string Action { get; private set; }31 30 internal object Entity { get; private set; } 32 internal PluginInfrastructureEventArgs(string action, object entity) { 33 this.Action = action; 31 internal PluginInfrastructureEventArgs(object entity) { 34 32 this.Entity = entity; 33 } 34 35 private static PluginInfrastructureEventArgs emptyArgs = new PluginInfrastructureEventArgs(string.Empty); 36 internal static PluginInfrastructureEventArgs Empty { 37 get { return emptyArgs; } 35 38 } 36 39 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/PluginManager.cs
r2790 r2922 36 36 /// </summary> 37 37 internal sealed class PluginManager : MarshalByRefObject { 38 /// <summary> 39 /// Event handler for actions in the plugin manager. 40 /// </summary> 41 internal event EventHandler<PluginInfrastructureEventArgs> Action; 38 internal event EventHandler<PluginInfrastructureEventArgs> PluginLoaded; 39 internal event EventHandler<PluginInfrastructureEventArgs> PluginUnloaded; 40 internal event EventHandler<PluginInfrastructureEventArgs> Initializing; 41 internal event EventHandler<PluginInfrastructureEventArgs> Initialized; 42 internal event EventHandler<PluginInfrastructureEventArgs> ApplicationStarting; 43 internal event EventHandler<PluginInfrastructureEventArgs> ApplicationStarted; 42 44 43 45 private string pluginDir; … … 66 68 plugins = new List<PluginDescription>(); 67 69 applications = new List<ApplicationDescription>(); 68 Reset();69 }70 71 internal void Reset() {72 70 initialized = false; 73 if (plugins != null && plugins.Any(x => x.PluginState == PluginState.Loaded)) throw new InvalidOperationException("Reset() is not allowed while applications are active.");74 plugins.Clear();75 applications.Clear();76 71 } 77 72 … … 80 75 /// </summary> 81 76 internal void DiscoverAndCheckPlugins() { 82 On Action(new PluginInfrastructureEventArgs("Initializing", "PluginInfrastructure"));77 OnInitializing(PluginInfrastructureEventArgs.Empty); 83 78 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation; 84 79 setup.PrivateBinPath = pluginDir; … … 92 87 remoteValidator.PluginLoaded += 93 88 delegate(object sender, PluginInfrastructureEventArgs e) { 94 On Action(e);89 OnPluginLoaded(e); 95 90 }; 96 91 // get list of plugins and applications from the validator … … 98 93 plugins.AddRange(remoteValidator.Plugins); 99 94 applications.AddRange(remoteValidator.Applications); 100 OnAction(new PluginInfrastructureEventArgs("Initialized", "PluginInfrastructure"));101 95 } 102 96 finally { … … 104 98 AppDomain.Unload(pluginDomain); 105 99 // unload all plugins 106 foreach (var pluginDescription in plugins.Where(x => x.PluginState == PluginState.Loaded)) 100 foreach (var pluginDescription in plugins.Where(x => x.PluginState == PluginState.Loaded)) { 107 101 pluginDescription.Unload(); 102 OnPluginUnloaded(new PluginInfrastructureEventArgs(pluginDescription)); 103 } 108 104 initialized = true; 105 OnInitialized(PluginInfrastructureEventArgs.Empty); 109 106 } 110 107 } … … 122 119 // and remotely tell it to start the application 123 120 124 OnA ction(new PluginInfrastructureEventArgs("Starting application",appInfo));121 OnApplicationStarting(new PluginInfrastructureEventArgs(appInfo)); 125 122 AppDomain applicationDomain = null; 126 123 try { … … 134 131 applicationManager.PluginUnloaded += applicationManager_PluginUnloaded; 135 132 applicationManager.PrepareApplicationDomain(applications, plugins); 136 OnA ction(new PluginInfrastructureEventArgs("Started application",appInfo));133 OnApplicationStarted(new PluginInfrastructureEventArgs(appInfo)); 137 134 applicationManager.Run(appInfo); 138 135 } … … 150 147 // can be started or stopped at the same time 151 148 lock (locker) { 149 // also unload the matching plugin description in this AppDomain 152 150 plugins.First(x => x.Equals(desc)).Unload(); 153 151 } 154 On Action(new PluginInfrastructureEventArgs(e.Action, e.Entity));152 OnPluginUnloaded(e); 155 153 } 156 154 … … 161 159 // can be started or stopped at the same time 162 160 lock (locker) { 161 // also load the matching plugin description in this AppDomain 163 162 plugins.First(x => x.Equals(desc)).Load(); 164 163 } 165 OnAction(new PluginInfrastructureEventArgs(e.Action, e.Entity)); 166 } 167 168 private void OnAction(PluginInfrastructureEventArgs e) { 169 if (Action != null) { 170 Action(this, e); 171 } 172 } 164 OnPluginLoaded(e); 165 } 166 167 #region event raising methods 168 private void OnPluginLoaded(PluginInfrastructureEventArgs e) { 169 if (PluginLoaded != null) { 170 PluginLoaded(this, e); 171 } 172 } 173 174 private void OnPluginUnloaded(PluginInfrastructureEventArgs e) { 175 if (PluginUnloaded != null) { 176 PluginUnloaded(this, e); 177 } 178 } 179 180 private void OnInitializing(PluginInfrastructureEventArgs e) { 181 if (Initializing != null) { 182 Initializing(this, e); 183 } 184 } 185 186 private void OnInitialized(PluginInfrastructureEventArgs e) { 187 if (Initialized != null) { 188 Initialized(this, e); 189 } 190 } 191 192 private void OnApplicationStarting(PluginInfrastructureEventArgs e) { 193 if (ApplicationStarting != null) { 194 ApplicationStarting(this, e); 195 } 196 } 197 198 private void OnApplicationStarted(PluginInfrastructureEventArgs e) { 199 if (ApplicationStarted != null) { 200 ApplicationStarted(this, e); 201 } 202 } 203 #endregion 173 204 174 205 // infinite lease time 175 206 /// <summary> 176 /// Initializes the life time service with infinite lease time.207 /// Make sure that the plugin manager is never disposed (necessary for cross-app-domain events) 177 208 /// </summary> 178 209 /// <returns><c>null</c>.</returns> -
trunk/sources/HeuristicLab.PluginInfrastructure/Manager/PluginValidator.cs
r2815 r2922 504 504 IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType); 505 505 plugin.OnLoad(); 506 OnPluginLoaded(new PluginInfrastructureEventArgs( "Plugin loaded", plugin.Name));506 OnPluginLoaded(new PluginInfrastructureEventArgs(desc)); 507 507 } 508 508 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Starter/SplashScreen.cs
r2790 r2922 46 46 this.manager = manager; 47 47 48 manager.Action += managerActionEventHandler; 48 manager.ApplicationStarted += new EventHandler<PluginInfrastructureEventArgs>(manager_ApplicationStarted); 49 manager.ApplicationStarting += new EventHandler<PluginInfrastructureEventArgs>(manager_ApplicationStarting); 50 manager.Initializing += new EventHandler<PluginInfrastructureEventArgs>(manager_Initializing); 51 manager.Initialized += new EventHandler<PluginInfrastructureEventArgs>(manager_Initialized); 52 manager.PluginLoaded += new EventHandler<PluginInfrastructureEventArgs>(manager_PluginLoaded); 53 manager.PluginUnloaded += new EventHandler<PluginInfrastructureEventArgs>(manager_PluginUnloaded); 49 54 50 55 infoLabel.Text = initialText; … … 67 72 } 68 73 74 void manager_PluginUnloaded(object sender, PluginInfrastructureEventArgs e) { 75 UpdateMessage("Unloaded " + e.Entity); 76 } 77 78 void manager_PluginLoaded(object sender, PluginInfrastructureEventArgs e) { 79 UpdateMessage("Loaded " + e.Entity); 80 } 81 82 void manager_Initialized(object sender, PluginInfrastructureEventArgs e) { 83 UpdateMessage("Initialized"); 84 } 85 86 void manager_Initializing(object sender, PluginInfrastructureEventArgs e) { 87 UpdateMessage("Initializing"); 88 } 89 90 void manager_ApplicationStarting(object sender, PluginInfrastructureEventArgs e) { 91 UpdateMessage("Starting " + e.Entity); 92 } 93 94 void manager_ApplicationStarted(object sender, PluginInfrastructureEventArgs e) { 95 UpdateMessage("Started " + e.Entity); 96 } 97 69 98 private void SetInfoText(string text) { 70 99 if (InvokeRequired) Invoke((Action<string>)SetInfoText, text); … … 74 103 } 75 104 76 private void managerActionEventHandler(object sender, PluginInfrastructureEventArgs e) {105 private void UpdateMessage(string msg) { 77 106 if (InvokeRequired) { 78 Invoke(( EventHandler<PluginInfrastructureEventArgs>)managerActionEventHandler, sender, e);107 Invoke((Action<string>)UpdateMessage, msg); 79 108 } else { 80 109 ResetFadeTimer(); 81 string info = e.Action + ": " + e.Entity; 82 SetInfoText(info); 110 SetInfoText(msg); 83 111 Application.DoEvents(); // force immediate update of splash screen control 84 112 } … … 103 131 fadeTimer.Stop(); 104 132 fadeTimer.Dispose(); 105 manager.Action -= managerActionEventHandler; // remove event before calling close 133 // remove event before calling close 134 manager.ApplicationStarted -= new EventHandler<PluginInfrastructureEventArgs>(manager_ApplicationStarted); 135 manager.ApplicationStarting -= new EventHandler<PluginInfrastructureEventArgs>(manager_ApplicationStarting); 136 manager.Initializing -= new EventHandler<PluginInfrastructureEventArgs>(manager_Initialized); 137 manager.Initialized -= new EventHandler<PluginInfrastructureEventArgs>(manager_Initializing); 138 manager.PluginLoaded -= new EventHandler<PluginInfrastructureEventArgs>(manager_PluginLoaded); 139 manager.PluginUnloaded -= new EventHandler<PluginInfrastructureEventArgs>(manager_PluginUnloaded); 106 140 Close(); 107 141 } -
trunk/sources/HeuristicLab.PluginInfrastructure/Starter/StarterForm.cs
r2790 r2922 93 93 ListViewItem selected = applicationsListView.SelectedItems[0]; 94 94 if (selected == pluginManagerListViewItem) { 95 try { 96 Cursor = Cursors.AppStarting; 97 InstallationManagerForm form = new InstallationManagerForm(); 98 this.Visible = false; 99 form.ShowDialog(this); 100 // RefreshApplicationsList(); 101 this.Visible = true; 102 } 103 finally { 104 Cursor = Cursors.Arrow; 95 if (pluginManager.Plugins.Any(x => x.PluginState == PluginState.Loaded)) { 96 MessageBox.Show("Installation Manager cannot be started while another HeuristicLab application is active." + Environment.NewLine + 97 "Please stop all HeuristicLab applications and try again."); 98 } else { 99 try { 100 Cursor = Cursors.AppStarting; 101 InstallationManagerForm form = new InstallationManagerForm(); 102 this.Visible = false; 103 form.ShowDialog(this); 104 // RefreshApplicationsList(); 105 this.Visible = true; 106 } 107 finally { 108 Cursor = Cursors.Arrow; 109 } 105 110 } 106 111 } else { … … 118 123 do { 119 124 try { 120 if (!abortRequested) 125 if (!abortRequested) { 126 SetCursor(Cursors.AppStarting); 121 127 pluginManager.Run(app); 128 } 122 129 stopped = true; 123 130 } … … 127 134 Thread.Sleep(5000); // sleep 5 seconds before autorestart 128 135 } 136 finally { 137 SetCursor(Cursors.Default); 138 } 129 139 } while (!abortRequested && !stopped && app.AutoRestart); 130 140 }); 131 141 t.SetApartmentState(ApartmentState.STA); // needed for the AdvancedOptimizationFrontent 132 142 t.Start(); 143 } 144 145 private void SetCursor(Cursor cursor) { 146 if (InvokeRequired) Invoke((Action<Cursor>)SetCursor, cursor); 147 else { 148 Cursor = cursor; 149 } 133 150 } 134 151 -
trunk/sources/HeuristicLab.PluginInfrastructure/app.config
r2860 r2922 30 30 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 31 31 bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" 32 maxBufferPoolSize="524288" maxReceivedMessageSize=" 65536"32 maxBufferPoolSize="524288" maxReceivedMessageSize="10000000" 33 33 messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" 34 34 allowCookies="false"> 35 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="1 6384"35 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="10000000" 36 36 maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 37 37 <reliableSession ordered="true" inactivityTimeout="00:10:00" … … 47 47 openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" 48 48 bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" 49 maxBufferPoolSize="524288" maxReceivedMessageSize=" 65536"49 maxBufferPoolSize="524288" maxReceivedMessageSize="10000000" 50 50 messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" 51 51 allowCookies="false"> 52 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="1 6384"52 <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="10000000" 53 53 maxBytesPerRead="4096" maxNameTableCharCount="16384" /> 54 54 <reliableSession ordered="true" inactivityTimeout="00:10:00"
Note: See TracChangeset
for help on using the changeset viewer.