FixAntenna/NetCore/FixEngine/SessionParameters.cs (892 lines of code) (raw):

// Copyright (c) 2021 EPAM Systems // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using Epam.FixAntenna.Constants.Fixt11; using Epam.FixAntenna.NetCore.Common; using Epam.FixAntenna.NetCore.Common.Logging; using Epam.FixAntenna.NetCore.Common.Utils; using Epam.FixAntenna.NetCore.Configuration; using Epam.FixAntenna.NetCore.FixEngine.Session.Common; using Epam.FixAntenna.NetCore.Helpers; using Epam.FixAntenna.NetCore.Message; using Epam.FixAntenna.NetCore.Message.Format; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using Epam.FixAntenna.NetCore.FixEngine.Scheduler; using static Epam.FixAntenna.NetCore.Configuration.Config; namespace Epam.FixAntenna.NetCore.FixEngine { /// <summary> /// This bean contains all session level configuration for current session. /// <p/> /// It is possible to confiniently define a list of custom FIX fields that will be added to each message. /// For more complex message customization take a look at <see cref="AbstractFixSessionFactory"/> /// </summary> /// <seealso cref="AbstractFixSessionFactory"> for more precise customization </seealso> public class SessionParameters : ICloneable { public const int DefaultSequenceNum = 0; internal const int MaxSessionIdLength = 200; private static readonly ILog Log = LogFactory.GetLog(typeof(SessionParameters)); private int? _port; private Config _configuration; public SessionId SessionId { get; private set; } = new SessionId("Sender", "Target"); public SessionParameters(Config config) { _configuration = config; FixVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, FixVersion.Fix42); } public SessionParameters() { try { _configuration = (Config)Config.GlobalConfiguration.Clone(); } catch (Exception e) { throw new Exception(e.Message, e); } FixVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, FixVersion.Fix42); } /// <summary> /// Gets or sets Configuration. /// </summary> public Config Configuration { get => _configuration; set => _configuration = (Config)value.Clone(); } /// <summary> /// Gets FIX version. /// </summary> public FixVersion FixVersion { get => FixVersionContainer.FixVersion; set => FixVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, value); } /// <summary> /// Sets FIX version /// </summary> /// <param name="fixVersion"></param> public void FixVersionFromString(string fixVersion) { if (string.IsNullOrEmpty(fixVersion)) { throw new ArgumentException("Fix version is empty"); } try { var version = FixVersion.GetInstanceByMessageVersion(fixVersion); FixVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, version); } catch (ArgumentException) { FixVersionContainer = FixVersionContainer.GetFixVersionContainer(fixVersion, _configuration); } } /// <summary> /// Gets or sets the App version. /// </summary> public FixVersion AppVersion { get => AppVersionContainer?.FixVersion; set { if (value == null) { AppVersionContainer = null; } else { AppVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, value); } } } /// <summary> /// Set FixVersionContainer by provided appVersion string. /// </summary> /// <param name="appVersion"></param> public void AppVersionFromString(string appVersion) { if (string.IsNullOrEmpty(appVersion)) { throw new ArgumentException("App version is empty"); } if (int.TryParse(appVersion, out int intVersion)) { var version = FixVersion.GetInstanceByFixtVersion(intVersion); AppVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, version); } else { try { var version = FixVersion.GetInstanceByMessageVersion(appVersion); AppVersionContainer = FixVersionContainer.GetFixVersionContainer(_configuration, version); } catch (ArgumentException) { AppVersionContainer = FixVersionContainer.GetFixVersionContainer(appVersion, _configuration); } } } public FixVersionContainer FixVersionContainer { get; set; } public FixVersionContainer AppVersionContainer { get; set; } /// <summary> /// Gets or sets user defined fields. /// <p/> /// If this list is not empty, Engine add it to each outgoing message. /// </summary> public FixMessage UserDefinedFields { get; set; } = new FixMessage(); /// <summary> /// Add user defined field. /// </summary> /// <param name="tag"></param> /// <param name="value"></param> public void AddHeaderField(int tag, byte[] value) { UserDefinedFields.AddTag(tag, value); } /// <summary> /// Gets or sets sender comp id. /// </summary> public string SenderCompId { get => SessionId.Sender; set => SessionId.Sender = value; } /// <summary> /// Gets or sets target comp id. /// </summary> public string TargetCompId { get => SessionId.Target; set => SessionId.Target = value; } /// <summary> /// Gets or sets sender sub id. /// </summary> public string SenderSubId { get; set; } /// <summary> /// Gets or sets target sub id. /// </summary> public string TargetSubId { get; set; } /// <summary> /// Gets or sets sender location id. /// </summary> public string SenderLocationId { get; set; } /// <summary> /// Gets or sets target location id. /// </summary> public string TargetLocationId { get; set; } ///<summary> ///Change session identifier ///</summary> ///<param name="sessionId"> unique string value. maximum 200 characters length. Allowed characters: a-z, A-Z, 0-9, '.', /// '-', '_', ' '(space) and '!' </param> public void SetSessionId(string sessionId) { CheckSessionId(sessionId); SessionId.CustomSessionId = sessionId; } private void CheckSessionId(string sessionId) { if (sessionId == null) { throw new ArgumentException("sessionId is null"); } if (!Regex.IsMatch(sessionId, @"^[_a-zA-Z0-9\-\. !]+$")) { throw new ArgumentException("Invalid sessionId: '" + sessionId + "'. Please use only letters, digits, '_', '-' and ' '"); } if (sessionId.Length > MaxSessionIdLength) { throw new ArgumentException("sessionId is too long: '" + sessionId + "'. Please use smaller (<200 characters)"); } } public string SessionQualifier { get => SessionId.Qualifier; set { SessionId.Qualifier = value; if (_configuration.GetPropertyAsBoolean(Config.SuppressSessionQualifierTagInLogonMessage)) { OutgoingLoginMessage.RemoveTag(_configuration.GetPropertyAsInt(Config.LogonMessageSessionQualifierTag)); } else if (!string.IsNullOrEmpty(value)) { OutgoingLoginMessage.UpdateValue(_configuration.GetPropertyAsInt(Config.LogonMessageSessionQualifierTag), value, IndexedStorage.MissingTagHandling.AddIfNotExists); } else { OutgoingLoginMessage.RemoveTag(_configuration.GetPropertyAsInt(Config.LogonMessageSessionQualifierTag)); } } } public bool IsCustomSessionId => SessionId.IsCustomSessionId; /// <summary> /// Gets or sets heartbeat interval. /// </summary> public int HeartbeatInterval { get; set; } = 30; /// <summary> /// Gets or sets host. /// </summary> public string Host { get; set; } public string BindIP { get; set; } /// <summary> /// Returns true if the Port is set. /// </summary> public bool HasPort => _port != null; /// <summary> /// Gets or sets port. /// </summary> public int? Port { get => _port ?? 0; set => _port = value; } /// <summary> /// Gets or sets force sequence reset option. /// </summary> public ForceSeqNumReset ForceSeqNumReset { get { try { var propertyValue = Configuration.GetProperty(Config.ForceSeqNumReset); if (string.IsNullOrEmpty(propertyValue)) { ParamSources.Instance.Set(Config.ForceSeqNumReset, ParamSource.Default, SessionId.ToString()); propertyValue = ForceSeqNumReset.Never.ToString(); } if (!(Enum.TryParse(propertyValue, true, out ForceSeqNumReset forceSeqNumReset) && Enum.IsDefined(typeof(ForceSeqNumReset), forceSeqNumReset))) { Log.Warn("Invalid forceSeqNumReset parameter."); ParamSources.Instance.Set(Config.ForceSeqNumReset, ParamSource.Default, SessionId.ToString()); return ForceSeqNumReset.Never; } return forceSeqNumReset; } catch (ArgumentException) { Log.Warn("Invalid forceSeqNumReset parameter."); return ForceSeqNumReset.Never; } } set => Configuration.SetProperty(Config.ForceSeqNumReset, value.ToString()); } public string UserName { set => OutgoingLoginMessage.UpdateValue(Tags.Username, value, IndexedStorage.MissingTagHandling.AddIfNotExists); get => OutgoingLoginMessage.GetTagValueAsString(Tags.Username); } public string Password { get => OutgoingLoginMessage.GetTagValueAsString(Tags.Password); set => OutgoingLoginMessage.UpdateValue(Tags.Password, value, IndexedStorage.MissingTagHandling.AddIfNotExists); } public string IncomingUserName => IncomingLoginMessage.GetTagValueAsString(Tags.Username); public string IncomingPassword => IncomingLoginMessage.GetTagValueAsString(Tags.Password); /// <summary> /// Gets or sets incoming login fields. /// </summary> /// <value> FixMessage list of field </value> /// <remarks>Engine use IncomingLoginMessage only for acceptor session.</remarks> public FixMessage IncomingLoginMessage { get; set; } = new FixMessage(); /// <summary> /// Gets or sets outgoing login fields. /// <p/> /// This parameter used only for initiator session, /// Engine added outgoingLoginFixMessage to login message. /// </summary> /// <value> list of fields </value> public FixMessage OutgoingLoginMessage { get; set; } = new FixMessage(); /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, string value) { OutgoingLoginMessage.AddTag(tag, value); } /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, byte[] value) { OutgoingLoginMessage.AddTag(tag, value); } /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, byte[] value, int offset, int length) { OutgoingLoginMessage.AddTag(tag, value, offset, length); } /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, long value) { OutgoingLoginMessage.AddTag(tag, value); } /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, double value, int precision) { OutgoingLoginMessage.AddTag(tag, value, precision); } /// <summary> /// Add field to outgoing login fields list. /// </summary> /// <param name="tag"> </param> /// <param name="value"> </param> public void AddOutgoingLoginField(int tag, DateTimeOffset value, FixDateFormatterFactory.FixDateType type) { OutgoingLoginMessage.AddCalendarTag(tag, value, type); } /// <summary> /// Creates and returns a copy of this object. /// </summary> /// <returns> SessionParameters </returns> public object Clone() { var @params = (SessionParameters)base.MemberwiseClone(); @params.SessionId = (SessionId)SessionId.Clone(); @params.UserDefinedFields = UserDefinedFields.DeepClone(false, UserDefinedFields.IsUserOwned); @params.IncomingLoginMessage = IncomingLoginMessage.DeepClone(false, IncomingLoginMessage.IsUserOwned); @params.OutgoingLoginMessage = OutgoingLoginMessage.DeepClone(false, OutgoingLoginMessage.IsUserOwned); @params.ForceSeqNumReset = ForceSeqNumReset; @params.FixVersionContainer = FixVersionContainer; @params.AppVersionContainer = AppVersionContainer; @params.IncomingSequenceNumber = IncomingSequenceNumber; @params.OutgoingSequenceNumber = OutgoingSequenceNumber; @params.Configuration = Configuration; @params.CustomLoader = CustomLoader; @params.Destinations = new List<DnsEndPoint>(Destinations); return @params; } /// <summary> /// Creates and returns a copy of this object. /// The engine calls this method when the specific parameters should be serialized to properties. /// </summary> public Dictionary<string, string> ToProperties() { var properties = new Dictionary<string, string>(); if (SenderCompId != null) { properties.Add("senderCompID", SenderCompId); } if (TargetCompId != null) { properties.Add("targetCompID", TargetCompId); } if (!string.IsNullOrEmpty(SessionQualifier)) { properties.Add("sessionQualifier", SessionQualifier); } if (IsCustomSessionId) { properties.Add(Properties.SessionIdKey, SessionId.ToString()); } if (Host != null) { properties.Add("host", Host); } properties.Add("port", Convert.ToString(Port)); properties.Add("FixVersion", FixVersion.MessageVersion); properties.Add("lastSeqNumResetTimestamp", LastSeqNumResetTimestamp.ToString()); properties.Add("inSeqNumsForNextConnect", IncomingSequenceNumber.ToString()); properties.Add("outSeqNumsForNextConnect", OutgoingSequenceNumber.ToString()); return properties; } /// <summary> /// Creates the SessionParameters from properties. /// The engine calls this method when the stored parameters should be de-serialized from properties. /// </summary> /// <param name="properties"> the properties </param> public void FromProperties(IDictionary<string, string> properties) { if (properties.ContainsKey(Properties.SessionIdKey)) { SetSessionId(properties.GetValueOrDefault(Properties.SessionIdKey)); } SenderCompId = properties.GetValueOrDefault("senderCompID"); TargetCompId = properties.GetValueOrDefault("targetCompID"); if (properties.ContainsKey("sessionQualifier")) { SessionQualifier = properties.GetValueOrDefault("sessionQualifier"); } if (properties.ContainsKey("senderSubID")) { SenderSubId = properties.GetValueOrDefault("senderSubID"); } if (properties.ContainsKey("senderLocationID")) { SenderLocationId = properties.GetValueOrDefault("senderLocationID"); } if (properties.ContainsKey("targetSubID")) { TargetSubId = properties.GetValueOrDefault("targetSubID"); } if (properties.ContainsKey("targetLocationID")) { TargetLocationId = properties.GetValueOrDefault("targetLocationID"); } if (properties.ContainsKey("host")) { Host = properties.GetValueOrDefault("host"); } if (properties.ContainsKey("port")) { Port = Convert.ToInt32(properties.GetValueOrDefault("port", "3000")); } if (properties.ContainsKey("bindIP")) { BindIP = properties.GetValueOrDefault("bindIP"); } SetDestinationsIfPresent(properties); if (properties.ContainsKey("appVersion")) { AppVersionFromString(properties.GetValueOrDefault("appVersion")); } if (properties.ContainsKey("fixVersion")) { FixVersionFromString(properties.GetValueOrDefault("fixVersion")); } if (properties.ContainsKey("heartbeatInterval")) { HeartbeatInterval = Convert.ToInt32(properties.GetValueOrDefault("heartbeatInterval")); } if (properties.ContainsKey("lastSeqNumResetTimestamp")) { try { LastSeqNumResetTimestamp = Convert.ToInt64(properties.GetValueOrDefault("lastSeqNumResetTimestamp")); } catch (FormatException) { throw new ArgumentException( "Error while trying to parse the last sequence reset date and time stored in the persistent storage. " + "The last sequence reset date and time are treated as empty. " + "Session's sequence numbers will be synchronized"); } } if (properties.ContainsKey("FixMessage")) { UserDefinedFields = RawFixUtil.GetFixMessage(properties.GetValueOrDefault("FixMessage").AsByteArray()); } if (properties.ContainsKey("incomingLoginFixMessage")) { IncomingLoginMessage = RawFixUtil.GetFixMessage(properties.GetValueOrDefault("incomingLoginFixMessage", string.Empty).AsByteArray()); } if (properties.ContainsKey("outgoingLoginFixMessage")) { OutgoingLoginMessage = RawFixUtil.GetFixMessage(properties.GetValueOrDefault("outgoingLoginFixMessage", string.Empty).AsByteArray()); } if (properties.ContainsKey("username")) { UserName = properties.GetValueOrDefault("username"); } if (properties.ContainsKey("password")) { Password = properties.GetValueOrDefault("password"); } if (properties.ContainsKey("inSeqNumsForNextConnect")) { IncomingSequenceNumber = Convert.ToInt64(properties.GetValueOrDefault("inSeqNumsForNextConnect", "-1")); } else if (properties.ContainsKey("incomingSequenceNumber")) { IncomingSequenceNumber = Convert.ToInt64(properties.GetValueOrDefault("incomingSequenceNumber")); } else { IncomingSequenceNumber = DefaultSequenceNum; ParamSources.Instance.Set("incomingSequenceNumber", ParamSource.Default, SessionId.ToString()); } if (properties.ContainsKey("outSeqNumsForNextConnect")) { OutgoingSequenceNumber = Convert.ToInt64(properties.GetValueOrDefault("outSeqNumsForNextConnect", "-1")); } else if (properties.ContainsKey("outgoingSequenceNumber")) { OutgoingSequenceNumber = Convert.ToInt64(properties.GetValueOrDefault("outgoingSequenceNumber")); } else { OutgoingSequenceNumber = DefaultSequenceNum; ParamSources.Instance.Set("outgoingSequenceNumber", ParamSource.Default, SessionId.ToString()); } } public void SetDestinationsIfPresent(IDictionary<string, string> properties) { var index = 0; while (properties.ContainsKey($"socketConnectAddress_{index}")) { var key = $"socketConnectAddress_{index}"; var value = properties.GetValueOrDefault(key); var address = value; var delimiterPos = address.IndexOf(':'); var host = address.Substring(0, delimiterPos); var port = Convert.ToInt32(address.Substring(delimiterPos + 1)); AddDestination(host, port); index++; } } /// <summary> /// Gets or sets last seq num reset timestamp. /// </summary> public long LastSeqNumResetTimestamp { get; set; } public bool IsSetSeqNumsOnNextConnect => IsSetInSeqNumsOnNextConnect && SetOutSeqNumsOnNextConnect; public bool IsSetInSeqNumsOnNextConnect => IncomingSequenceNumber > 0; //TODO: naming public bool SetOutSeqNumsOnNextConnect => OutgoingSequenceNumber > 0; public long IncomingSequenceNumber { set; get; } = DefaultSequenceNum; public long OutgoingSequenceNumber { set; get; } = DefaultSequenceNum; public void DisableInSeqNumsOnNextConnect() //TODO: naming { IncomingSequenceNumber = DefaultSequenceNum; } public void DisableOutSeqNumsOnNextConnect() //TODO: naming { OutgoingSequenceNumber = DefaultSequenceNum; } public override string ToString() { return string.Join(", ", ToProperties()); } public override bool Equals(object o) { if (this == o) { return true; } if (o == null || GetType() != o.GetType()) { return false; } var that = (SessionParameters)o; return SessionId.Equals(that.SessionId) && Destinations.SequenceEqual(that.Destinations) && HeartbeatInterval == that.HeartbeatInterval && HeartbeatInterval == that.HeartbeatInterval && Port == that.Port && Host == that.Host && FixVersionContainer.Equals(that.FixVersionContainer) && AppVersionContainer == null ? that.AppVersionContainer == null : AppVersionContainer.Equals(that.AppVersionContainer) && Configuration == that.Configuration && UserDefinedFields == that.UserDefinedFields && IncomingLoginMessage == that.IncomingLoginMessage && OutgoingLoginMessage == that.OutgoingLoginMessage && SenderCompId == that.SenderCompId && SenderLocationId == that.SenderLocationId && SenderSubId == that.SenderSubId && TargetCompId == that.TargetCompId && TargetLocationId == that.TargetLocationId && TargetSubId == that.TargetSubId && CustomLoader == that.CustomLoader; } public override int GetHashCode() { return new HashCodeBuilder(17, 37) .Append(SessionId) .Append(SenderCompId) .Append(SenderLocationId) .Append(SenderSubId) .Append(TargetCompId) .Append(TargetLocationId) .Append(TargetSubId) .Append(HeartbeatInterval) .Append(FixVersionContainer) .Append(AppVersionContainer) .Append(Host) .Append(Port) .Append(UserDefinedFields) .Append(IncomingLoginMessage) .Append(OutgoingLoginMessage) .Append(Configuration) .Append(Destinations) .Append(CustomLoader) .Build(); } private void ReplaceDestination(string oldHost, int oldPort, string newHost, int newPort) { if (!string.IsNullOrEmpty(oldHost) && oldPort > 0) { Destinations.Remove(new DnsEndPoint(oldHost, oldPort)); } if (!string.IsNullOrEmpty(newHost) && newPort > 0) { Destinations.Insert(0, new DnsEndPoint(newHost, newPort)); } } /// <summary> /// Gets alternative(backup) destinations for initiator. </summary> /// <value> list of alternative destinations. </value> public IList<DnsEndPoint> Destinations { get; private set; } = new List<DnsEndPoint>(); /// <summary> /// Add alternative(backup) destination for initiator. </summary> /// <param name="host"> backup host </param> /// <param name="port"> backup port </param> public void AddDestination(string host, int port) { Destinations.Add(new DnsEndPoint(host, port)); } /// <summary> /// Add alternative(backup) destination for initiator. </summary> /// <param name="destination"> backup address </param> public void AddDestination(DnsEndPoint destination) { Destinations.Add(destination); } /// <summary> /// Add alternative(backup) destinations for initiator. </summary> /// <param name="destinations"> backup addresses </param> public void AddAllDestinations(ICollection<DnsEndPoint> destinations) { ((List<DnsEndPoint>)Destinations).AddRange(destinations); } /// <summary> /// Remove alternative(backup) destination from connections list. </summary> /// <param name="host"> backup host </param> /// <param name="port"> backup port </param> public void RemoveDestination(string host, int port) { Destinations.Remove(new DnsEndPoint(host, port)); } /// <summary> /// Remove alternative(backup) destination from connections list. </summary> /// <param name="destination"> backup address </param> public void RemoveDestination(DnsEndPoint destination) { Destinations.Remove(destination); } /// <summary> /// Remove alternative(backup) destinations from connections list. </summary> /// <param name="destinations"> backup addresses </param> public void RemoveAllDestinations(ICollection<DnsEndPoint> destinations) { Destinations = Destinations.Except(destinations) as IList<DnsEndPoint>; //TODO: check this } /// <summary> /// Creates initiator fix session. /// <p/> /// User can use /// <c>StandardFixSessionFactory.GetFactory(SessionParameters).CreateInitiatorSession(SessionParameters)</c> /// instead this method. /// </summary> /// @deprecated use <seealso cref="SessionParameters.CreateAcceptorSession()"/> or <seealso cref="CreateInitiatorSession"/> instead public IFixSession CreateNewFixSession() { return StandardFixSessionFactory.GetFactory(this).CreateInitiatorSession(this); } /// <summary> /// Creates acceptor session in disconnected state /// </summary> /// <returns> FIX session </returns> public IFixSession CreateAcceptorSession() { return StandardFixSessionFactory.GetFactory(this).CreateAcceptorSession(this); } /// <summary> /// Creates initiator session /// </summary> /// <returns> FIX session </returns> public IFixSession CreateInitiatorSession() { return StandardFixSessionFactory.GetFactory(this).CreateInitiatorSession(this); } /// <summary> /// Creates scheduled initiator session /// </summary> /// <returns> FIX session </returns> public IScheduledFixSession CreateScheduledInitiatorSession() { return (IScheduledFixSession)StandardFixSessionFactory.GetFactory(this).CreateInitiatorSession(this); } public void PrintConfiguration() { if (Log.IsDebugEnabled) { Log.Debug((new ParameterLogHelper(this)).PrintConfiguration(this)); } } public bool IsNeedToIncludeLastProcessed() { return Configuration.GetPropertyAsBoolean(Config.IncludeLastProcessed, false) && FixVersion.CompareTo(FixVersion.Fix42) >= 0; // include only if configuration says yes and current session FixVersion is 4.2 or above } /// <summary> /// Gets or sets the loader for loading configurable classes. /// </summary> /// <value> </value> public Func<string, object> CustomLoader { set; get; } private class ParameterLogHelper { private readonly SessionParameters _outerInstance; internal StringBuilder StringWriter; internal const int MaxParamLength = 80; public ParameterLogHelper(SessionParameters outerInstance) { _outerInstance = outerInstance; StringWriter = new StringBuilder(1024); } public string PrintConfiguration(SessionParameters sessionParameters) { var configuration = sessionParameters.Configuration; var sessionId = _outerInstance.SessionId.ToString(); WriteNewLine(); PrintTitle("Start configuration of session: " + _outerInstance.SessionId + "(" + sessionId + ")"); // session parameters PrintParameter("Host", sessionParameters.Host, ParamSources.Instance.Get("host", sessionId)); // special processing for SSL port PrintParameter("Port", sessionParameters.Port, sessionParameters.Port == configuration.GetPropertyAsInt(Config.SslPort) ? ParamSources.Instance.Get(SslPort, sessionId) : ParamSources.Instance.Get("port", sessionId)); PrintParameter("HBi", sessionParameters.HeartbeatInterval, ParamSources.Instance.Get("heartbeatInterval", sessionId)); PrintParameter("FixVersion", sessionParameters.FixVersionContainer, ParamSources.Instance.Get("fixVersion", sessionId)); PrintParameter("appVersion", sessionParameters.AppVersionContainer, ParamSources.Instance.Get("appVersion", sessionId)); PrintParameter("incomingSequenceNumber", sessionParameters.IncomingSequenceNumber, ParamSources.Instance.Get("incomingSequenceNumber", sessionId)); PrintParameter("outgoingSequenceNumber", sessionParameters.OutgoingSequenceNumber, ParamSources.Instance.Get("outgoingSequenceNumber", sessionId)); PrintParameter("outgoingLoginFixMessage", sessionParameters.OutgoingLoginMessage.ToPrintableString(), ParamSources.Instance.Get("outgoingLoginFixMessage", sessionId)); PrintParameter("resetInSeqNumsOnNextConnect", sessionParameters.IsSetInSeqNumsOnNextConnect, ParamSources.Instance.Get("incomingSequenceNumber", sessionId)); PrintParameter("resetOutSeqNumsOnNextConnect", sessionParameters.SetOutSeqNumsOnNextConnect, ParamSources.Instance.Get("outgoingSequenceNumber", sessionId)); PrintParameter("customHandlersLoaderClass", sessionParameters.CustomLoader, ParamSources.Default); for (var i = 0; i < _outerInstance.Destinations.Count; i++) { var key = $"socketConnectAddress_{i}"; var inetAddress = _outerInstance.Destinations[i]; PrintParameter(key, $"{inetAddress.Host}:{inetAddress.Port}", ParamSources.Instance.Get(key, sessionId)); } PrintParameter(MaxMessageSize); PrintParameter(MaxMessagesToSendInBatch); PrintParameter(AutoreconnectAttempts); PrintParameter(AutoreconnectDelayInMs); PrintParameter(ConnectAddress); PrintParameter(InMemoryQueue); PrintParameter(QueueThresholdSize); PrintParameter(OutgoingStorageIndexed); PrintParameter(Config.ForceSeqNumReset, sessionParameters.ForceSeqNumReset, ParamSources.Instance.Get(Config.ForceSeqNumReset, sessionId)); PrintParameter(ResendRequestNumberOfMessagesLimit); PrintParameter(MaxRequestResendInBlock); PrintParameter(CyclicSwitchBackupConnection); PrintParameter(EnableAutoSwitchToBackupConnection); PrintParameter(ResetOnSwitchToBackup); PrintParameter(ResetOnSwitchToPrimary); PrintParameter(SendCpuAffinity); PrintParameter(RecvCpuAffinity); PrintParameter(Accuracy); PrintParameter(TimestampsPrecisionInTags); PrintParameter(EnableMessageRejecting); PrintParameter(EnableNagle); PrintParameter(ForcedLogoffTimeout); PrintParameter(IncludeLastProcessed); PrintParameter(LoginWaitTimeout); PrintParameter(TimestampsInLogs); PrintParameter(TimestampsPrecisionInLogs); PrintParameter(LogFilesTimeZone); // begin SSL PrintParameter(RequireSsl, configuration.GetPropertyAsBoolean(RequireSsl, defaultValue: false, warnToLog: true), ParamSources.Instance.Get(RequireSsl, sessionId)); var sslPort = configuration.GetProperty(SslPort, new ValidatorIntegerList(1, 65535), nullable: true, warnInLog: true); PrintParameter(SslPort, sslPort, ParamSources.Instance.Get(SslPort, sessionId)); PrintParameter(SslCertificate); PrintParameter(SslCaCertificate); PrintParameter(SslCheckCertificateRevocation, configuration.GetPropertyAsBoolean(SslCheckCertificateRevocation, defaultValue: false, warnToLog: true), ParamSources.Instance.Get(SslCheckCertificateRevocation, sessionId)); PrintParameter(SslProtocol); PrintParameter(SslServerName); PrintParameter(SslValidatePeerCertificate, configuration.GetPropertyAsBoolean(SslValidatePeerCertificate, defaultValue: false, warnToLog: true), ParamSources.Instance.Get(SslValidatePeerCertificate, sessionId)); // end SSL PrintParameter(TradePeriodBegin); PrintParameter(TradePeriodEnd); PrintParameter(TradePeriodTimeZone); PrintTitle("End configuration of session: " + _outerInstance.SessionId); return StringWriter.ToString(); } public override string ToString() { return StringWriter.ToString(); } public ParameterLogHelper PrintTitle(string title) { var paramLength = title.Length; var diff = (MaxParamLength - paramLength) / 2; while (diff-- > 0) { StringWriter.Append("-"); } StringWriter.Append(" ").Append(title).Append(" "); diff = (MaxParamLength - paramLength) / 2; while (diff-- > 0) { StringWriter.Append("-"); } StringWriter.AppendLine(); return this; } public ParameterLogHelper WriteNewLine() { StringWriter.AppendLine(); return this; } public ParameterLogHelper PrintParameter(string paramName) { var value = _outerInstance.Configuration.GetProperty(paramName); var source = ParamSources.Instance.Get(paramName, _outerInstance.SessionId.ToString()); PrintParameter(paramName, value, source); return this; } public ParameterLogHelper PrintParameter(string paramName, object paramValue, string paramSource) { if (paramValue == null) { paramValue = string.Empty; } var paramLength = paramName.Length; StringWriter.Append(paramName); var diff = MaxParamLength - paramLength; while (diff-- > 0) { StringWriter.Append("."); } StringWriter.Append(paramValue); StringWriter.Append(" ("); StringWriter.Append(paramSource); StringWriter.Append(")"); StringWriter.AppendLine(); return this; } } /// <summary> /// Return true if parameter object describe the same FIX session. /// </summary> public bool IsSimilar(SessionParameters other) { return IsSimilar(other, null); } /// <summary> /// Return true if parameter object describe the same FIX session. /// </summary> public bool IsSimilar(SessionParameters other, List<string> errors) { var result = true; if (!SenderCompId.Equals(other.SenderCompId)) { AddErrorDescription(errors, $"SenderCompId not similar: '{SenderCompId}' vs '{other.SenderCompId}'"); result = false; } if (!SenderLocationId?.Equals(other.SenderLocationId) ?? !string.IsNullOrEmpty(other.SenderLocationId)) { AddErrorDescription(errors, $"SenderLocationId not similar: '{SenderLocationId}' vs '{other.SenderLocationId}'"); result = false; } if (!SenderSubId?.Equals(other.SenderSubId) ?? !string.IsNullOrEmpty(other.SenderSubId)) { AddErrorDescription(errors, $"SenderSubId not similar: '{SenderSubId}' vs '{other.SenderSubId}'"); result = false; } if (!TargetCompId.Equals(other.TargetCompId)) { AddErrorDescription(errors, $"TargetCompId not similar: '{TargetCompId}' vs '{other.TargetCompId}'"); result = false; } if (!TargetSubId?.Equals(other.TargetSubId) ?? !string.IsNullOrEmpty(other.TargetSubId)) { AddErrorDescription(errors, $"TargetSubId not similar: '{TargetSubId}' vs '{other.TargetSubId}'"); result = false; } if (!TargetLocationId?.Equals(other.TargetLocationId) ?? !string.IsNullOrEmpty(other.TargetLocationId)) { AddErrorDescription(errors, $"TargetLocationId not similar: '{TargetLocationId}' vs '{other.TargetLocationId}'"); result = false; } if (!SessionQualifier?.Equals(other.SessionQualifier) ?? !string.IsNullOrEmpty(other.SessionQualifier)) { AddErrorDescription(errors, $"SessionQualifier not similar: '{SessionQualifier}' vs '{other.SessionQualifier}'"); result = false; } if (!FixVersionContainer.Similar(other.FixVersionContainer)) { AddErrorDescription(errors, $"FIXVersionContainer not similar: '{FixVersionContainer}' vs '{other.FixVersionContainer}'"); result = false; } if (!AppVersionContainer?.Similar(other.AppVersionContainer) ?? other.AppVersionContainer != null) { AddErrorDescription(errors, $"AppVersionContainer not similar: '{AppVersionContainer}' vs '{other.AppVersionContainer}'"); result = false; } var thisAddresses = Destinations; var otherAddresses = other.Destinations; if (thisAddresses.Count != otherAddresses.Count) { AddErrorDescription(errors, $"SocketConnectAddresses are different: '{string.Join(",", thisAddresses)}' vs '{string.Join(",", otherAddresses)}'"); result = false; } else { for (var i = 0; i < thisAddresses.Count; i++) { if (!thisAddresses[i].Equals(otherAddresses[i])) { AddErrorDescription(errors, $"SocketConnectAddresses are different: '{thisAddresses[i]}' vs '{otherAddresses[i]}'"); result = false; } } } return result; } private static void AddErrorDescription(List<string> errors, string msg) { if (Log.IsDebugEnabled) { Log.Debug(msg); } errors?.Add(msg); } } }