using System; using System.Collections.Generic; using System.Threading.Tasks; using JD.Adobe.Service.API; using JD.Adobe.Service.ExecutableManagement; using Microsoft.AspNet.SignalR; using Plume.Core.Events; using Plume.Core.Logging; using Plume.Interfaces.Services; using Shuttle.Api.ClientAPI; using Shuttle.Servers; using Shuttle.Servers.Hubs; using Shuttle.Servers.Managers.AsyncProviders; using Shuttle.Servers.Managers.Clients; using Shuttle.Servers.Managers.Events; using Shuttle.Servers.Managers.Items; using Shuttle.Servers.Managers.Notifications; namespace JD.Adobe.Service.Implementation { public abstract class AdobeService : IAdobeService { private readonly object _lock = new object(); private readonly HashSet _targets = new HashSet(); private readonly ISignalrServer _signalrServer; private readonly EventInvoker _eventInvoker; private IAdobeEvents _adobeEvents; protected readonly AdobeExecutableManager _adobeExecutableManager; protected IAsyncProvider _asyncProvider; protected IClientManager _clientManager; protected bool _isInitialized = false; protected ILogger Logger { get; } public event Action OnTargetAdded; public event Action OnTargetRemoved; public event Action OnProjectFileChanged; public event Action OnTargetFocused; protected AdobeService(ILogger logger, string serverUrl) { if (serverUrl == null) { throw new ArgumentNullException(nameof(serverUrl)); } Logger = logger ?? throw new ArgumentNullException(nameof(logger)); _signalrServer = new SignalrServer(serverUrl); _eventInvoker = new EventInvoker(Logger); _adobeExecutableManager = new AdobeExecutableManager(Logger); } protected abstract void InitializeImpl(IServiceContext context); public void Initialize(IServiceContext context) { _asyncProvider = new AsyncProvider(); var aeHubContext = GlobalHost.ConnectionManager.GetHubContext(); var psHubContext = GlobalHost.ConnectionManager.GetHubContext(); _clientManager = new ClientManager(Logger, aeHubContext, psHubContext); _clientManager.OnTargetConnection += AddTarget; _clientManager.OnTargetDisconnection += RemoveTarget; var eventManager = new EventManager(Logger, _clientManager); eventManager.OnAppFocused += OnAppFocused; eventManager.OnProjectOpened += OnProjectOpened; eventManager.OnProjectClosed += OnProjectClosedEvent; _adobeEvents = eventManager; _signalrServer.Initialize(Logger, _clientManager, itemDepository: new ItemDepository(Logger, _asyncProvider), operationNotificationListener: new OperationNotificationListener(Logger, _asyncProvider), eventNotificationListener: eventManager); _signalrServer.Start(); _adobeExecutableManager.Initialize(); InitializeImpl(context); } public ICollection GetAvailableTargets() { ICollection results; lock (_lock) { results = new List(_targets); } return results; } public abstract Task StartNewInstance(AdobeApplicationType appType); public void Dispose() { _clientManager.OnTargetConnection -= AddTarget; _clientManager.OnTargetDisconnection -= RemoveTarget; _adobeEvents.OnAppFocused -= OnAppFocused; _adobeEvents.OnProjectOpened -= OnProjectOpened; _adobeEvents.OnProjectClosed -= OnProjectClosedEvent; _signalrServer.Dispose(); } private void AddTarget(TargetAppInfo target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (TargetAppInfo.IsEmpty(target)) { throw new ArgumentException($"{nameof(target)} cannot be the empty target.", nameof(target)); } bool added; lock (_lock) { added = _targets.Add(target); } if (!added) { throw new ArgumentException($"Target '{target.ShuttleId}' ({target.ConnectionId}) was already known by {this.GetType().Name}."); } _eventInvoker.SafeInvokeEventAsync(OnTargetAdded, target); } private void RemoveTarget(TargetAppInfo target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } if (TargetAppInfo.IsEmpty(target)) { throw new ArgumentException($"{nameof(target)} cannot be the empty target.", nameof(target)); } bool removed; lock (_lock) { removed = _targets.Remove(target); } if (!removed) { throw new ArgumentException($"Target '{target.ShuttleId}' ({target.ConnectionId}) was not known by {this.GetType().Name} in the first place."); } _eventInvoker.SafeInvokeEventAsync(OnTargetRemoved, target); } protected void ThrowIfUnknown(TargetAppInfo target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } bool isKnown; lock (_lock) { isKnown = _targets.Contains(target); } if (isKnown) { return; } throw new ArgumentException($"Unknown target '{target.ShuttleId}' ({target.ConnectionId})"); } private void OnProjectClosedEvent(TargetAppInfo target, string projectFile) { _eventInvoker.SafeInvokeEventAsync(OnProjectFileChanged, target, projectFile); } private void OnProjectOpened(TargetAppInfo target, string projectFile) { _eventInvoker.SafeInvokeEventAsync(OnProjectFileChanged, target, projectFile); } private void OnAppFocused(TargetAppInfo target) { _eventInvoker.SafeInvokeEventAsync(OnTargetFocused, target); } } }