Example #1
0
        /// <summary>
        /// Attempts to execute a plan.
        /// </summary>
        /// <param name="disabledPluginKeys">Plugins that must be disabled.</param>
        /// <param name="stoppedPluginKeys">Plugins that must be stopped.</param>
        /// <param name="runningPluginKeys">Plugins that must be running.</param>
        /// <returns>A <see cref="IExecutionPlanError"/> that details the error if any.</returns>
        public IExecutionPlanResult Execute(IEnumerable <IPluginInfo> disabledPluginKeys, IEnumerable <IPluginInfo> stoppedPluginKeys, IEnumerable <IPluginInfo> runningPluginKeys)
        {
            if (PluginCreator == null)
            {
                throw new InvalidOperationException(R.PluginCreatorIsNull);
            }
            if (ServiceReferencesBinder == null)
            {
                throw new InvalidOperationException(R.PluginConfiguratorIsNull);
            }

            int nbIntersect;

            nbIntersect = disabledPluginKeys.Intersect(stoppedPluginKeys).Count();
            if (nbIntersect != 0)
            {
                throw new CKException(R.DisabledAndStoppedPluginsIntersect, nbIntersect);
            }
            nbIntersect = disabledPluginKeys.Intersect(runningPluginKeys).Count();
            if (nbIntersect != 0)
            {
                throw new CKException(R.DisabledAndRunningPluginsIntersect, nbIntersect);
            }
            nbIntersect = stoppedPluginKeys.Intersect(runningPluginKeys).Count();
            if (nbIntersect != 0)
            {
                throw new CKException(R.StoppedAndRunningPluginsIntersect, nbIntersect);
            }

            List <PluginProxy> toDisable = new List <PluginProxy>();
            List <PluginProxy> toStop    = new List <PluginProxy>();
            List <PluginProxy> toStart   = new List <PluginProxy>();

            foreach (IPluginInfo k in disabledPluginKeys)
            {
                PluginProxy p = EnsureProxy(k);
                if (p.Status != RunningStatus.Disabled)
                {
                    toDisable.Add(p);
                    if (p.Status != RunningStatus.Stopped)
                    {
                        toStop.Add(p);
                    }
                }
            }
            foreach (IPluginInfo k in stoppedPluginKeys)
            {
                PluginProxy p = EnsureProxy(k);
                if (p.Status != RunningStatus.Stopped)
                {
                    toStop.Add(p);
                }
            }
            // The lists toDisable and toStop are correctly filled.
            // A plugin can be in both lists if it must be stopped and then disabled.

            // Now, we attempt to activate the plugins that must run: if an error occurs,
            // we leave and return the error since we did not change anything.
            foreach (IPluginInfo k in runningPluginKeys)
            {
                PluginProxy p = EnsureProxy(k);
                if (!p.IsLoaded)
                {
                    if (!p.TryLoad(_serviceHost, PluginCreator))
                    {
                        Debug.Assert(p.LoadError != null);
                        _serviceHost.LogMethodError(PluginCreator.Method, p.LoadError);
                        // Unable to load the plugin: leave now.
                        return(new ExecutionPlanResult()
                        {
                            Culprit = p.PluginKey, Status = ExecutionPlanResultStatus.LoadError
                        });
                    }
                    Debug.Assert(p.LoadError == null);
                    Debug.Assert(p.Status == RunningStatus.Disabled);
                    _newlyLoadedPlugins.Add(p);
                }
                if (p.Status != RunningStatus.Started)
                {
                    toStart.Add(p);
                }
            }
            // The toStart list is ready: plugins inside are loaded without error.

            // We stop all "toStop" plugin.
            // Their "stop" methods will be called.
            foreach (PluginProxy p in toStop)
            {
                if (p.Status > RunningStatus.Stopped)
                {
                    try
                    {
                        SetPluginStatus(p, RunningStatus.Stopping);
                        p.RealPlugin.Stop();
                        _log.Debug(String.Format("The {0} plugin has been successfully stopped.", p.PublicName));
                    }
                    catch (Exception ex)
                    {
#if DEBUG
                        //Helps the developper identify the culprit of exception
                        Debugger.Break();
#endif
                        _log.ErrorFormat("There has been a problem when stopping the {0} plugin.", ex, p.PublicName);
                        _serviceHost.LogMethodError(p.GetImplMethodInfoStop(), ex);
                    }
                }
            }

            // We un-initialize all "toStop" plugin.
            // Their "Teardown" methods will be called.
            // After that, they are all "stopped".
            foreach (PluginProxy p in toStop)
            {
                try
                {
                    if (p.Status > RunningStatus.Stopped)
                    {
                        SetPluginStatus(p, RunningStatus.Stopped);
                        p.RealPlugin.Teardown();
                        _log.Debug(String.Format("The {0} plugin has been successfully torn down.", p.PublicName));
                    }
                }
                catch (Exception ex)
                {
#if DEBUG
                    //Helps the developper identify the culprit of exceptions
                    Debugger.Break();
#endif
                    _log.ErrorFormat("There has been a problem when tearing down the {0} plugin.", ex, p.PublicName);
                    _serviceHost.LogMethodError(p.GetImplMethodInfoTeardown(), ex);
                }
            }
            Debug.Assert(toStop.All(p => p.Status <= RunningStatus.Stopped));


            // Prepares the plugins to start so that they become the implementation
            // of their Service and are at least stopped (instead of disabled).
            foreach (PluginProxy p in toStart)
            {
                ServiceProxyBase service = p.Service;
                // The call to service.SetImplementation, sets the implementation and takes
                // the _status of the service into account: this status is at most Stopped
                // since we necessarily stopped the previous implementation (if any) above.
                if (service != null)
                {
                    Debug.Assert(service.Status <= RunningStatus.Stopped);
                    service.SetPluginImplementation(p);
                }
                // This call will trigger an update of the service status.
                if (p.Status == RunningStatus.Disabled)
                {
                    SetPluginStatus(p, RunningStatus.Stopped);
                }
            }

            // Now that services have been associated to their new implementation (in Stopped status), we
            // can disable the plugins that must be disabled.
            foreach (PluginProxy p in toDisable)
            {
                SetPluginStatus(p, RunningStatus.Disabled);
                try
                {
                    p.DisposeIfDisposable();
                }
                catch (Exception ex)
                {
#if DEBUG
                    //Helps the developper identify the culprit of exceptions
                    Debugger.Break();
#endif
                    _log.ErrorFormat("There has been a problem when disposing the {0} plugin.", ex, p.PublicName);
                    _serviceHost.LogMethodError(p.GetImplMethodInfoDispose(), ex);
                }
            }

            // Before starting
            for (int i = 0; i < toStart.Count; i++)
            {
                PluginProxy p = toStart[i];
                // We configure plugin's edition properties.
                if (PluginConfigurator != null)
                {
                    PluginConfigurator(p);
                }

                SetPluginStatus(p, RunningStatus.Starting);
                IPluginSetupInfo info = new IPluginSetupInfo();
                try
                {
                    p.RealPlugin.Setup(info);
                    info.Clear();
                    _log.Debug(String.Format("The {0} plugin has been successfully set up.", p.PublicName));
                }
                catch (Exception ex)
                {
#if DEBUG
                    //Helps the developper identify the culprit of exceptions
                    Debugger.Break();
#endif
                    _log.ErrorFormat("There has been a problem when setting up the {0} plugin.", ex, p.PublicName);
                    _serviceHost.LogMethodError(p.GetImplMethodInfoSetup(), ex);

                    // Revoking the call to Setup for all plugins that haven't been started yet.
                    //Will pass the plugin to states : Stopping and then Stopped
                    for (int j = 0; j <= i; j++)
                    {
                        RevokeSetupCall(toStart[j]);
                    }

                    info.Error = ex;
                    return(new ExecutionPlanResult()
                    {
                        Culprit = p.PluginKey, Status = ExecutionPlanResultStatus.SetupError, SetupInfo = info
                    });
                }
            }

            // Since we are now ready to start new plugins, it is now time to make the external world
            // aware of the existence of any new plugins and configure them to run.
            foreach (PluginProxy p in _newlyLoadedPlugins)
            {
                _loadedPlugins.Add(p.PluginKey.UniqueId, p);
            }
            Debug.Assert(ServiceReferencesBinder != null);
            try
            {
                var listNew = new ReadOnlyCollectionOnICollection <PluginProxy>(_newlyLoadedPlugins);
                //var disabled = new ReadOnlyCollectionAdapter<IPluginProxy, PluginProxy>( toDisable );
                ServiceReferencesBinder(listNew);
            }
            catch (Exception ex)
            {
                _serviceHost.LogMethodError(ServiceReferencesBinder.Method, ex);
            }
            _newlyLoadedPlugins.Clear();

            for (int i = 0; i < toStart.Count; i++)
            {
                PluginProxy p = toStart[i];
                try
                {
                    SetPluginStatus(p, RunningStatus.Started);
                    p.RealPlugin.Start();
                    _log.Debug(String.Format("The {0} plugin has been successfully started.", p.PublicName));
                }
                catch (Exception ex)
                {
                    // Emitted as low level log.
                    _log.ErrorFormat("There has been a problem when starting the {0} plugin.", ex, p.PublicName);

                    // Emitted as a log event.
                    _serviceHost.LogMethodError(p.GetImplMethodInfoStart(), ex);

                    //All the plugins already started  when the exception was thrown have to be stopped + teardown (including this one in exception)
                    for (int j = 0; j <= i; j++)
                    {
                        RevokeStartCall(toStart[j]);
                    }

                    // Revoking the call to Setup for all plugins that hadn't been started when the exception occured.
                    for (int j = i + 1; j < toStart.Count; j++)
                    {
                        RevokeSetupCall(toStart[j]);
                    }

                    return(new ExecutionPlanResult()
                    {
                        Culprit = p.PluginKey, Status = ExecutionPlanResultStatus.LoadError, Error = ex
                    });
                }
            }
            return(new ExecutionPlanResult());
        }