/// <summary>
        /// Asynchronously discovers plugins.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns a
        /// <see cref="IEnumerable{PluginDiscoveryResult}" /> from the target.</returns>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public async Task <IEnumerable <PluginDiscoveryResult> > DiscoverAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (_results != null)
            {
                return(_results);
            }

            await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                if (_results != null)
                {
                    return(_results);
                }

                _pluginFiles = GetPluginFiles(cancellationToken);
                var results = new List <PluginDiscoveryResult>();

                for (var i = 0; i < _pluginFiles.Count; ++i)
                {
                    var pluginFile = _pluginFiles[i];

                    var result = new PluginDiscoveryResult(pluginFile);

                    results.Add(result);
                }

                _results = results;
            }
            finally
            {
                _semaphore.Release();
            }

            return(_results);
        }
Esempio n. 2
0
        /// <summary>
        /// Creates a plugin from the discovered plugin.
        /// We firstly check the cache for the operation claims for the given request key.
        /// If there is a valid cache entry, and it does contain the requested operation claim, then we start the plugin, and if need be update the cache value itself.
        /// If there is a valid cache entry, and it does NOT contain the requested operation claim, then we return a null.
        /// If there is no valid cache entry or an invalid one, we start the plugin as normally, return an active plugin even if the requested claim is not available, and write a cache entry.
        /// </summary>
        /// <param name="result">plugin discovery result</param>
        /// <param name="requestedOperationClaim">The requested operation claim</param>
        /// <param name="requestKey">plugin request key</param>
        /// <param name="packageSourceRepository">package source repository</param>
        /// <param name="serviceIndex">service index</param>
        /// <param name="cancellationToken">cancellation token</param>
        /// <returns>A plugin creation result, null if the requested plugin cannot handle the given operation claim</returns>
        private async Task <Tuple <bool, PluginCreationResult> > TryCreatePluginAsync(
            PluginDiscoveryResult result,
            OperationClaim requestedOperationClaim,
            PluginRequestKey requestKey,
            string packageSourceRepository,
            JObject serviceIndex,
            CancellationToken cancellationToken)
        {
            PluginCreationResult pluginCreationResult = null;
            var cacheEntry = new PluginCacheEntry(_pluginsCacheDirectoryPath.Value, result.PluginFile.Path, requestKey.PackageSourceRepository);

            ConcurrencyUtilities.ExecuteWithFileLocked(cacheEntry.CacheFileName, cacheEntry.LoadFromFile);

            if (cacheEntry.OperationClaims == null || cacheEntry.OperationClaims.Contains(requestedOperationClaim))
            {
                try
                {
                    if (result.PluginFile.State.Value == PluginFileState.Valid)
                    {
                        var plugin = await _pluginFactory.GetOrCreateAsync(
                            result.PluginFile.Path,
                            PluginConstants.PluginArguments,
                            new RequestHandlers(),
                            _connectionOptions,
                            cancellationToken);

                        var utilities = await PerformOneTimePluginInitializationAsync(plugin, cancellationToken);

                        // We still make the GetOperationClaims call even if we have the operation claims cached. This is a way to self-update the cache.
                        var operationClaims = await _pluginOperationClaims.GetOrAdd(
                            requestKey,
                            key => new Lazy <Task <IReadOnlyList <OperationClaim> > >(() =>
                                                                                      GetPluginOperationClaimsAsync(
                                                                                          plugin,
                                                                                          packageSourceRepository,
                                                                                          serviceIndex,
                                                                                          cancellationToken))).Value;

                        if (!EqualityUtility.SequenceEqualWithNullCheck(operationClaims, cacheEntry.OperationClaims))
                        {
                            cacheEntry.OperationClaims = operationClaims;

                            await utilities.Value.DoOncePerPluginLifetimeAsync(
                                nameof(PluginCacheEntry),
                                () => ConcurrencyUtilities.ExecuteWithFileLockedAsync(
                                    cacheEntry.CacheFileName,
                                    action: async lockedToken =>
                            {
                                await cacheEntry.UpdateCacheFileAsync();

                                return(Task.FromResult <object>(null));
                            },
                                    token: cancellationToken),
                                cancellationToken);
                        }

                        pluginCreationResult = new PluginCreationResult(
                            plugin,
                            utilities.Value,
                            operationClaims);
                    }
                    else
                    {
                        pluginCreationResult = new PluginCreationResult(result.Message);
                    }
                }
                catch (Exception e)
                {
                    pluginCreationResult = new PluginCreationResult(
                        string.Format(CultureInfo.CurrentCulture,
                                      Strings.Plugin_ProblemStartingPlugin,
                                      result.PluginFile.Path,
                                      e.Message),
                        e);
                }
            }

            return(new Tuple <bool, PluginCreationResult>(pluginCreationResult != null, pluginCreationResult));
        }
Esempio n. 3
0
        /// <summary>
        /// Creates a plugin from the given pluginDiscoveryResult.
        /// This plugin's operations will be source agnostic ones (Authentication)
        /// </summary>
        /// <param name="pluginDiscoveryResult">plugin discovery result</param>
        /// <param name="requestedOperationClaim">The requested operation claim</param>
        /// <param name="cancellationToken">cancellation token</param>
        /// <returns>A plugin creation result, null if the requested plugin cannot handle the given operation claim</returns>
        public Task <Tuple <bool, PluginCreationResult> > TryGetSourceAgnosticPluginAsync(PluginDiscoveryResult pluginDiscoveryResult, OperationClaim requestedOperationClaim, CancellationToken cancellationToken)
        {
            if (pluginDiscoveryResult == null)
            {
                throw new ArgumentNullException(nameof(pluginDiscoveryResult));
            }

            return(TryCreatePluginAsync(
                       pluginDiscoveryResult,
                       requestedOperationClaim,
                       new PluginRequestKey(pluginDiscoveryResult.PluginFile.Path, "Source-Agnostic"),
                       packageSourceRepository: null,
                       serviceIndex: null,
                       cancellationToken: cancellationToken));
        }
Esempio n. 4
0
        /// <summary>
        /// Asynchronously discovers plugins.
        /// </summary>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns a
        /// <see cref="IEnumerable{PluginDiscoveryResult}" /> from the target.</returns>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="cancellationToken" />
        /// is cancelled.</exception>
        public async Task <IEnumerable <PluginDiscoveryResult> > DiscoverAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (_results != null)
            {
                return(_results);
            }

            await _semaphore.WaitAsync(cancellationToken);

            try
            {
                if (_results != null)
                {
                    return(_results);
                }

                _pluginFiles = GetPluginFiles(cancellationToken);
                var results = new List <PluginDiscoveryResult>();

                for (var i = 0; i < _pluginFiles.Count; ++i)
                {
                    var    pluginFile = _pluginFiles[i];
                    string message    = null;

                    switch (pluginFile.State)
                    {
                    case PluginFileState.Valid:
                        break;

                    case PluginFileState.NotFound:
                        message = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Plugin_FileNotFound,
                            pluginFile.Path);
                        break;

                    case PluginFileState.InvalidFilePath:
                        message = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Plugin_InvalidPluginFilePath,
                            pluginFile.Path);
                        break;

                    case PluginFileState.InvalidEmbeddedSignature:
                        message = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Plugin_InvalidEmbeddedSignature,
                            pluginFile.Path);
                        break;

                    default:
                        throw new NotImplementedException();
                    }

                    var result = new PluginDiscoveryResult(pluginFile, message);

                    results.Add(result);
                }

                _results = results;
            }
            finally
            {
                _semaphore.Release();
            }

            return(_results);
        }