using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Persistence { public class JsonUserRepository : IUserRepository { private readonly ConcurrentDictionary _fileLocks = new ConcurrentDictionary(); private SemaphoreSlim GetLock(string filename) { return _fileLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); } /// /// Gets the name of the repository /// /// The name. public string Name { get { return "Json"; } } /// /// Gets the json serializer. /// /// The json serializer. private readonly IJsonSerializer _jsonSerializer; private readonly string _dataPath; /// /// Initializes a new instance of the class. /// /// The app paths. /// The json serializer. /// The log manager. /// /// appPaths /// or /// jsonSerializer /// public JsonUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) { if (appPaths == null) { throw new ArgumentNullException("appPaths"); } if (jsonSerializer == null) { throw new ArgumentNullException("jsonSerializer"); } _jsonSerializer = jsonSerializer; _dataPath = Path.Combine(appPaths.DataPath, "users"); } /// /// Opens the connection to the database /// /// Task. public Task Initialize() { return Task.FromResult(true); } /// /// Save a user in the repo /// /// The user. /// The cancellation token. /// Task. /// user public async Task SaveUser(User user, CancellationToken cancellationToken) { if (user == null) { throw new ArgumentNullException("user"); } if (cancellationToken == null) { throw new ArgumentNullException("cancellationToken"); } cancellationToken.ThrowIfCancellationRequested(); if (!Directory.Exists(_dataPath)) { Directory.CreateDirectory(_dataPath); } var path = Path.Combine(_dataPath, user.Id + ".json"); var semaphore = GetLock(path); await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { _jsonSerializer.SerializeToFile(user, path); } finally { semaphore.Release(); } } /// /// Retrieve all users from the database /// /// IEnumerable{User}. public IEnumerable RetrieveAllUsers() { try { return Directory.EnumerateFiles(_dataPath, "*.json", SearchOption.TopDirectoryOnly) .Select(i => _jsonSerializer.DeserializeFromFile(i)); } catch (IOException) { return new List(); } } /// /// Deletes the user. /// /// The user. /// The cancellation token. /// Task. /// user public async Task DeleteUser(User user, CancellationToken cancellationToken) { if (user == null) { throw new ArgumentNullException("user"); } if (cancellationToken == null) { throw new ArgumentNullException("cancellationToken"); } cancellationToken.ThrowIfCancellationRequested(); var path = Path.Combine(_dataPath, user.Id + ".json"); var semaphore = GetLock(path); await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { File.Delete(path); } finally { semaphore.Release(); } } public void Dispose() { // Wait up to two seconds for any existing writes to finish var locks = _fileLocks.Values.ToList() .Where(i => i.CurrentCount == 1) .Select(i => i.WaitAsync(2000)); var task = Task.WhenAll(locks); Task.WaitAll(task); } } }