public async Task <IEnumerable <IPackage> > GetCatalogPackagesAsync(string filterText, Uri registryUrl = null) { IEnumerable <IPackage> packages = null; using (var semaphore = GetDatabaseSemaphore()) { // Wait until file is downloaded/parsed if another download is already in session. // Allows user to open multiple npm windows and show progress bar without file-in-use errors. bool success = semaphore.WaitOne(10); if (!success) { OnOutputLogged(Resources.ErrorCatalogInUse); // Return immediately so that the user can explicitly decide to refresh on failure. return(null); } try { registryUrl = registryUrl ?? await GetRegistryUrl(); RegistryFileMapping registryFileMapping = null; using (var db = new SQLiteConnection(DatabaseCacheFilePath)) { registryFileMapping = db.Table <RegistryFileMapping>().FirstOrDefault(info => info.RegistryUrl == registryUrl.ToString()); } if (registryFileMapping != null) { string registryFileLocation = Path.Combine(CachePath, registryFileMapping.DbFileLocation, RegistryCacheFilename); var packagesEnumerable = new DatabasePackageCatalogFilter(registryFileLocation).Filter(filterText); packages = await Task.Run(() => packagesEnumerable.ToList()); } } catch (Exception e) { OnOutputLogged(e.ToString()); throw; } finally { semaphore.Release(); } } return(packages); }
public override async Task <bool> ExecuteAsync() { var dbFilename = DatabaseCacheFilePath; bool catalogUpdated = false; string filename = null; string registryCacheDirectory = null; string registryCachePath = null; string registryCacheFilePath = null; // Use a semaphore instead of a mutex because await may return to a thread other than the calling thread. using (var semaphore = GetDatabaseSemaphore()) { // Wait until file is downloaded/parsed if another download is already in session. // Allows user to open multiple npm windows and show progress bar without file-in-use errors. bool success = await Task.Run(() => semaphore.WaitOne(TimeSpan.FromMinutes(5))); if (!success) { // Return immediately so that the user can explicitly decide to refresh on failure. return(false); } Uri registryUrl = await GetRegistryUrl(); OnOutputLogged(string.Format(Resources.InfoRegistryUrl, registryUrl)); try { DbVersion version = null; RegistryInfo registryInfo = null; RegistryFileMapping registryFileMapping = null; Directory.CreateDirectory(Path.GetDirectoryName(dbFilename)); using (var db = new SQLiteConnection(dbFilename)) { // prevent errors from occurring when table doesn't exist db.CreateCatalogTableIfNotExists(); version = db.Table <DbVersion>().FirstOrDefault(); registryFileMapping = db.Table <RegistryFileMapping>().FirstOrDefault(info => info.RegistryUrl == registryUrl.ToString()); } registryCacheDirectory = registryFileMapping != null ? registryFileMapping.DbFileLocation : Guid.NewGuid().ToString(); registryCachePath = Path.Combine(CachePath, registryCacheDirectory); registryCacheFilePath = Path.Combine(registryCachePath, RegistryCacheFilename); Directory.CreateDirectory(Path.GetDirectoryName(registryCacheFilePath)); if (File.Exists(registryCacheFilePath)) { using (var registryDb = new SQLiteConnection(registryCacheFilePath)) { // prevent errors from occurring when table doesn't exist registryDb.CreateRegistryTableIfNotExists(); registryInfo = registryDb.Table <RegistryInfo>().FirstOrDefault(); } } bool correctDatabaseSchema = version != null && version.Id == _databaseSchemaVersion; bool incrementalUpdate = correctDatabaseSchema && _forceDownload && registryInfo != null && registryInfo.Revision > 0; bool fullUpdate = correctDatabaseSchema && (registryInfo == null || registryInfo.Revision <= 0); if (!correctDatabaseSchema) { OnOutputLogged(Resources.InfoCatalogUpgrade); SafeDeleteFolder(CachePath); CreateCatalogDatabaseAndInsertEntries(dbFilename, registryUrl, registryCacheDirectory); filename = await UpdatePackageCache(registryUrl, CachePath); catalogUpdated = true; } else if (incrementalUpdate) { filename = await UpdatePackageCache(registryUrl, registryCachePath, registryInfo.Revision); catalogUpdated = true; } else if (fullUpdate) { CreateCatalogDatabaseAndInsertEntries(dbFilename, registryUrl, registryCacheDirectory); filename = await UpdatePackageCache(registryUrl, registryCachePath); catalogUpdated = true; } if (catalogUpdated) { var fileInfo = new FileInfo(filename); OnOutputLogged(String.Format(Resources.InfoReadingBytesFromPackageCache, fileInfo.Length, filename, fileInfo.LastWriteTime)); using (var reader = new StreamReader(filename)) { await Task.Run(() => ParseResultsAndAddToDatabase(reader, registryCacheFilePath, registryUrl.ToString())); } } using (var db = new SQLiteConnection(registryCacheFilePath)) { db.CreateRegistryTableIfNotExists(); ResultsCount = db.Table <CatalogEntry>().Count(); } } catch (Exception ex) { if (ex is StackOverflowException || ex is OutOfMemoryException || ex is ThreadAbortException || ex is AccessViolationException) { throw; } // assume the results are corrupted OnOutputLogged(ex.ToString()); throw; } finally { if (ResultsCount == null) { OnOutputLogged(string.Format(Resources.DownloadOrParsingFailed, CachePath)); SafeDeleteFolder(registryCacheDirectory); } else if (ResultsCount <= 0) { // Database file exists, but is corrupt. Delete database, so that we can download the file next time arround. OnOutputLogged(string.Format(Resources.DatabaseCorrupt, dbFilename)); SafeDeleteFolder(registryCacheDirectory); } semaphore.Release(); } } LastRefreshed = File.GetLastWriteTime(registryCacheFilePath); OnOutputLogged(String.Format(Resources.InfoCurrentTime, DateTime.Now)); OnOutputLogged(String.Format(Resources.InfoLastRefreshed, LastRefreshed)); if (ResultsCount != null) { OnOutputLogged(String.Format(Resources.InfoNumberOfResults, ResultsCount)); } return(true); }