internal ServiceCommand(IJellyfishContext context, IClock clock, string commandGroup = null, string commandName = null, string threadPoolKey = null, CommandPropertiesBuilder properties = null, ICircuitBreaker circuitBreaker = null, CommandMetrics metrics = null, CommandExecutionHook executionHook = null) { Contract.Requires(context != null); CommandState state = _states.GetOrAdd(this.GetType().FullName, (n) => ServiceCommandHelper.PrepareInternal(this.GetType(), context, commandGroup, commandName, properties, clock, metrics, circuitBreaker)); if (String.IsNullOrEmpty(state.CommandGroup)) { throw new ArgumentException("commandGroup can not be null or empty."); } Logger = context.GetService <ILoggerFactory>()?.CreateLogger(this.GetType().FullName) ?? EmptyLogger.Instance; CommandGroup = state.CommandGroup; CommandName = state.CommandName; _clock = clock ?? Clock.GetInstance(); // for test Properties = properties?.Build(CommandName) ?? new CommandProperties(CommandName); Metrics = metrics ?? CommandMetricsFactory.GetInstance(CommandName, CommandGroup, Properties, _clock); _circuitBreaker = circuitBreaker ?? (Properties.CircuitBreakerEnabled.Value ? CircuitBreakerFactory.GetOrCreateInstance(CommandName, Properties, Metrics, _clock) : new NoOpCircuitBreaker()); context.MetricsPublisher.CreateOrRetrievePublisherForCommand(CommandGroup, Metrics, _circuitBreaker); this._flags = state.Flags; _threadPoolKey = threadPoolKey ?? CommandGroup; _executionResult = new ExecutionResult(); _executionHook = executionHook ?? context.CommandExecutionHook; var executionPolicy = Properties.ExecutionIsolationStrategy.Value; if (executionPolicy == ExecutionIsolationStrategy.Semaphore) { _flags |= ServiceCommandOptions.SemaphoreExecutionStrategy; } if (executionPolicy == ExecutionIsolationStrategy.Thread) { _flags |= ServiceCommandOptions.ThreadExecutionStrategy; } if (Properties.RequestLogEnabled.Value) { _currentRequestLog = context.GetRequestLog(); } if ((_flags & ServiceCommandOptions.HasCacheKey) == ServiceCommandOptions.HasCacheKey && Properties.RequestCacheEnabled.Value) { _requestCache = context.GetCache <CacheItem>(CommandName); } }
internal static CommandState PrepareInternal(Type commandType, IJellyfishContext context, string commandGroup, string commandName, CommandPropertiesBuilder propertiesBuilder = null, IClock clock = null, CommandMetrics metrics = null, ICircuitBreaker circuitBreaker = null) { var state = new CommandState(); state.CommandName = commandName ?? commandType.FullName; state.CommandGroup = commandGroup ?? state.CommandName; clock = clock ?? Clock.GetInstance(); var properties = propertiesBuilder?.Build(state.CommandName) ?? new CommandProperties(state.CommandName); metrics = metrics ?? CommandMetricsFactory.GetInstance(state.CommandName, state.CommandGroup, properties, clock); circuitBreaker = circuitBreaker ?? (properties.CircuitBreakerEnabled.Value ? CircuitBreakerFactory.GetOrCreateInstance(state.CommandName, properties, metrics, clock) : new NoOpCircuitBreaker()); context.MetricsPublisher.CreateOrRetrievePublisherForCommand(state.CommandGroup, metrics, circuitBreaker); ServiceCommandOptions flags = ServiceCommandOptions.None; if (IsMethodImplemented(commandType, "GetFallback")) { flags |= ServiceCommandOptions.HasFallBack; } if (IsMethodImplemented(commandType, "GetCacheKey")) { flags |= ServiceCommandOptions.HasCacheKey; } state.Flags = flags; return(state); }
/// <summary> /// Construct a <see cref="ServiceCommand{T}"/>. /// <p> /// The CommandName will be derived from the implementing class name. /// Construct a <see cref="ServiceCommand{T}"/> with defined <see cref="CommandPropertiesBuilder"/> that allows injecting property /// and strategy overrides and other optional arguments. /// <p> /// NOTE: The CommandName is used to associate a <see cref="ServiceCommand{T}"/> /// with <see cref="ICircuitBreaker"/>, <see cref="CommandMetrics"/> and other objects. /// <p> /// Do not create multiple <see cref="ServiceCommand{T}"/> implementations with the same CommandName /// but different injected default properties as the first instantiated will win. /// <p> /// Properties passed in via <see cref="CommandPropertiesBuilder">Properties</see> are cached for the given CommandName for the life of the Process /// or until <see cref="IJellyfishContext.Reset"/> is called. Dynamic properties allow runtime changes. Read more on the <a href="https://github.com/Zenasoft/Jellyfish.Configuration"> Wiki</a>. /// </summary> /// <param name="context">Current jellyfish context</param> /// <param name="commandGroup"> /// used to group together multiple <see cref="ServiceCommand{T}"/> objects. <p/> /// The CommandGroup is used to represent a common relationship between commands. /// For example, a library or team name, the system all related commands interact with, common business purpose etc. /// </param> /// <param name="properties"></param> /// <param name="threadPoolKey"> /// used to identify the thread pool in which a <see cref="ServiceCommand{T}"/> executes. /// </param> internal protected ServiceCommand(IJellyfishContext context, string commandGroup = null, string commandName = null, CommandPropertiesBuilder properties = null, string threadPoolKey = null, CommandExecutionHook executionHook = null) : this(context, null, commandGroup, commandName, threadPoolKey, properties) { }