/// <summary> /// For tests only. /// </summary> internal ServiceProxyBase SetManualProxy(Type interfaceType, ServiceProxyBase proxy) { ServiceProxyBase current; if (_proxies.TryGetValue(interfaceType, out current)) { _proxies[interfaceType] = proxy; proxy.SetPluginImplementation(current.Implementation); } else { _proxies.Add(interfaceType, proxy); } proxy.Initialize(this, false); ApplyConfiguration(proxy); return(current); }
/// <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()); }