diff --git a/src/DynamoCore/Search/NodeSearchModel.cs b/src/DynamoCore/Search/NodeSearchModel.cs index 92149fddef5..fe95d4f1bf3 100644 --- a/src/DynamoCore/Search/NodeSearchModel.cs +++ b/src/DynamoCore/Search/NodeSearchModel.cs @@ -237,14 +237,7 @@ internal IEnumerable Search(string search, LuceneSearchUtilit if (luceneSearchUtility != null) { //The DirectoryReader and IndexSearcher have to be assigned after commiting indexing changes and before executing the Searcher.Search() method, otherwise new indexed info won't be reflected - if (luceneSearchUtility.writer != null) - { - luceneSearchUtility.dirReader = luceneSearchUtility.writer.GetReader(applyAllDeletes: true); - } - else - { - luceneSearchUtility.dirReader = DirectoryReader.Open(luceneSearchUtility.indexDir); - } + luceneSearchUtility.dirReader = luceneSearchUtility.writer != null ? luceneSearchUtility.writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(luceneSearchUtility.indexDir); luceneSearchUtility.Searcher = new IndexSearcher(luceneSearchUtility.dirReader); string searchTerm = search.Trim(); diff --git a/src/DynamoCore/Utilities/LuceneSearchUtility.cs b/src/DynamoCore/Utilities/LuceneSearchUtility.cs index dbf3274ce19..74f062d82c5 100644 --- a/src/DynamoCore/Utilities/LuceneSearchUtility.cs +++ b/src/DynamoCore/Utilities/LuceneSearchUtility.cs @@ -3,12 +3,8 @@ using System.IO; using System.Linq; using Dynamo.Configuration; -using Dynamo.Core; -using Dynamo.Events; -using Dynamo.Logging; using Dynamo.Models; using Dynamo.Search.SearchElements; -using Dynamo.Session; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Br; using Lucene.Net.Analysis.Cjk; @@ -95,6 +91,7 @@ public enum LuceneStorage /// /// Constructor for LuceneSearchUtility, it will use the storage type passed as parameter /// + /// /// internal LuceneSearchUtility(DynamoModel model, LuceneStartConfig config) { @@ -132,12 +129,13 @@ internal void InitializeLuceneConfig() /// /// Create index writer for followup doc indexing /// - internal void CreateLuceneIndexWriter() + /// Index open mode for Lucene index writer + internal void CreateLuceneIndexWriter(OpenMode mode = OpenMode.CREATE) { // Create an index writer IndexWriterConfig indexConfig = new IndexWriterConfig(LuceneConfig.LuceneNetVersion, Analyzer) { - OpenMode = OpenMode.CREATE + OpenMode = mode }; try { @@ -147,12 +145,12 @@ internal void CreateLuceneIndexWriter() { DisposeWriter(); - (ExecutionEvents.ActiveSession.GetParameterValue(ParameterKeys.Logger) as DynamoLogger).LogError($"LuceneNET LockObtainFailedException {ex}"); + dynamoModel.Logger.LogError($"LuceneNET LockObtainFailedException {ex}"); } catch (Exception ex) { - (ExecutionEvents.ActiveSession.GetParameterValue(ParameterKeys.Logger) as DynamoLogger).LogError($"LuceneNET Exception {ex}"); + dynamoModel.Logger.LogError($"LuceneNET Exception {ex}"); } } @@ -384,6 +382,9 @@ internal Analyzer CreateAnalyzerByLanguage(string language) } } + /// + /// Dispose Lucene index write objects and reuse other objects + /// internal void DisposeWriter() { writer?.Dispose(); @@ -395,7 +396,7 @@ internal void DisposeWriter() /// internal void DisposeAll() { - writer?.Dispose(); + DisposeWriter(); dirReader?.Dispose(); indexDir?.Dispose(); Analyzer?.Dispose(); @@ -418,6 +419,16 @@ internal void CommitWriterChanges() internal void AddNodeTypeToSearchIndex(NodeSearchElement node, Document doc) { if (addedFields == null) return; + // During DynamoModel initialization, the index writer should still be valid here + // If the index writer is null and index not locked, it means the index writer has been disposed, e.g. DynamoModel finished initialization + // If the index writer is null and index locked, it means another Dynamo session is currently updating the search index + // Try to create a new index writer to amend the index + if (writer == null && !IndexWriter.IsLocked(this.indexDir)) + { + CreateLuceneIndexWriter(OpenMode.CREATE_OR_APPEND); + } + // If the index writer is still null, skip the indexing + if (writer == null) return; SetDocumentFieldValue(doc, nameof(LuceneConfig.NodeFieldsEnum.FullCategoryName), node.FullCategoryName); SetDocumentFieldValue(doc, nameof(LuceneConfig.NodeFieldsEnum.Name), node.Name); diff --git a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs index ecdd683d23a..f9b7066b1e4 100644 --- a/src/DynamoCoreWpf/Controls/StartPage.xaml.cs +++ b/src/DynamoCoreWpf/Controls/StartPage.xaml.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using System.Diagnostics; using System.IO; +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -366,7 +367,7 @@ private void RefreshFileList(ObservableCollection files, IEnumerable filePaths) { files.Clear(); - foreach (var filePath in filePaths) + foreach (var filePath in filePaths.Where(x => x != null)) { try { diff --git a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs index 31b89dc0818..f1712501375 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs @@ -1,14 +1,3 @@ -using Dynamo.Configuration; -using Dynamo.Engine.CodeGeneration; -using Dynamo.Graph; -using Dynamo.Graph.Nodes; -using Dynamo.Graph.Nodes.CustomNodes; -using Dynamo.Graph.Workspaces; -using Dynamo.Logging; -using Dynamo.Models; -using Dynamo.Selection; -using Dynamo.Wpf.ViewModels.Core; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -20,6 +9,17 @@ using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Media; +using Dynamo.Configuration; +using Dynamo.Engine.CodeGeneration; +using Dynamo.Graph; +using Dynamo.Graph.Nodes; +using Dynamo.Graph.Nodes.CustomNodes; +using Dynamo.Graph.Workspaces; +using Dynamo.Logging; +using Dynamo.Models; +using Dynamo.Selection; +using Dynamo.Wpf.ViewModels.Core; +using Newtonsoft.Json; using Point = System.Windows.Point; using Size = System.Windows.Size; diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs index aca34008833..35a8b0a68f4 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerClientViewModel.cs @@ -1010,6 +1010,8 @@ internal virtual void InstallPackage(PackageDownloadHandle packageDownloadHandle } } SetPackageState(packageDownloadHandle, installPath); + // Dispose Index writer to avoid file lock after new package is installed + Search.LuceneSearch.LuceneUtilityNodeSearch.DisposeWriter(); Analytics.TrackEvent(Actions.Installed, Categories.PackageManagerOperations, $"{packageDownloadHandle?.Name}"); } catch (Exception e) diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs index 7947803ccca..73939d10f02 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackageManagerSearchViewModel.cs @@ -1028,14 +1028,7 @@ public void RefreshAndSearchAsync() if (!DynamoModel.IsTestMode) { - if (LuceneUtility.writer != null) - { - LuceneUtility.dirReader = LuceneUtility.writer.GetReader(applyAllDeletes: true); - } - else - { - LuceneUtility.dirReader = DirectoryReader.Open(LuceneUtility.indexDir); - } + LuceneUtility.dirReader = LuceneUtility.writer != null ? LuceneUtility.writer.GetReader(applyAllDeletes: true) : DirectoryReader.Open(LuceneUtility.indexDir); LuceneUtility.Searcher = new IndexSearcher(LuceneUtility.dirReader); LuceneUtility.CommitWriterChanges(); @@ -1417,7 +1410,7 @@ internal IEnumerable Search(string searchT var packages = new List(); //The DirectoryReader and IndexSearcher have to be assigned after commiting indexing changes and before executing the Searcher.Search() method,otherwise new indexed info won't be reflected - LuceneUtility.dirReader = LuceneUtility.writer?.GetReader(applyAllDeletes: true); + LuceneUtility.dirReader = LuceneUtility.writer != null ? LuceneUtility.writer.GetReader(applyAllDeletes: true): DirectoryReader.Open(LuceneUtility.indexDir); if (LuceneUtility.Searcher == null && LuceneUtility.dirReader != null) { @@ -1577,7 +1570,6 @@ internal void Close() InitialResultsLoaded = false; // reset the loading screen settings RequestShowFileDialog -= OnRequestShowFileDialog; nonHostFilter.ForEach(f => f.PropertyChanged -= filter_PropertyChanged); - LuceneUtility.DisposeAll(); } } } diff --git a/src/DynamoCoreWpf/ViewModels/PackageManager/PackagePathViewModel.cs b/src/DynamoCoreWpf/ViewModels/PackageManager/PackagePathViewModel.cs index adbc2ff7d43..4c0bcfb4c3b 100644 --- a/src/DynamoCoreWpf/ViewModels/PackageManager/PackagePathViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/PackageManager/PackagePathViewModel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Dynamo.Core; @@ -266,6 +266,8 @@ private void CommitChanges(object param) packageLoader.LoadNewCustomNodesAndPackages(newPaths, customNodeManager); } packagePathsEnabled.Clear(); + // Dispose node Index writer to avoid file lock after new package path applied and packages loaded. + Search.LuceneSearch.LuceneUtilityNodeSearch.DisposeWriter(); } internal void SetPackagesScheduledState(string packagePath, bool packagePathDisabled) diff --git a/src/DynamoUtilities/PathHelper.cs b/src/DynamoUtilities/PathHelper.cs index f93f3453c4b..393ac19df9b 100644 --- a/src/DynamoUtilities/PathHelper.cs +++ b/src/DynamoUtilities/PathHelper.cs @@ -37,6 +37,9 @@ public static Exception CreateFolderIfNotExist(string folderPath) { try { + // Do not even try when folder path is null or empty. + // This usually happens when system folder dialog is initialized with empty path + if (string.IsNullOrEmpty(folderPath)) return null; // When network path is access denied, the Directory.Exits however still // return true. // EnumerateDirectories operation is additional check diff --git a/src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs b/src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs index 8a566f3c793..1c28c53381c 100644 --- a/src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs +++ b/src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs @@ -187,14 +187,17 @@ private void UnSubscribePythonNodeEvents(PythonNodeBase node) private void UnSubscribeWorkspaceEvents() { - CurrentWorkspace.RequestPackageDependencies -= PythonDependencies.AddPythonPackageDependency; - CurrentWorkspace.NodeAdded -= OnNodeAdded; - CurrentWorkspace.NodeRemoved -= OnNodeRemoved; - CurrentWorkspace.Nodes - .Where(n => n is PythonNode) - .Cast() - .ToList() - .ForEach(n => UnSubscribePythonNodeEvents(n)); + if (CurrentWorkspace != null) + { + CurrentWorkspace.RequestPackageDependencies -= PythonDependencies.AddPythonPackageDependency; + CurrentWorkspace.NodeAdded -= OnNodeAdded; + CurrentWorkspace.NodeRemoved -= OnNodeRemoved; + CurrentWorkspace.Nodes + .Where(n => n is PythonNode) + .Cast() + .ToList() + .ForEach(n => UnSubscribePythonNodeEvents(n)); + } } private void UnsubscribeEvents()