/// <summary> /// Creates an argument of a specific type by parsing the string /// </summary> /// <returns>The parsed object.</returns> /// <param name="value">The string to parse.</param> /// <param name="targettype">The type to parse to.</param> public static object ArgumentFromString(string value, Type targettype) { if (targettype.IsEnum) { var entries = ExpandEnvironmentVariables(value ?? "") .Split('|') .Select(x => Enum.Parse(targettype, x, true)) .ToArray(); if (entries.Length == 1) { return(entries.First()); } else { return(Enum.ToObject( targettype, entries.Select(x => (int)Convert.ChangeType(x, typeof(int))).Sum() )); } } if (targettype.IsArray) { // TODO: Handle embedded comma values in strings? var args = (value ?? "") .Split(',') .Select(x => ArgumentFromString((x ?? string.Empty).Trim(), targettype.GetElementType())) .ToArray(); // Manually convert to the right type var res = Array.CreateInstance(targettype.GetElementType(), args.Length); Array.Copy(args, res, args.Length); return(res); } if ((targettype.IsArray || targettype == typeof(string)) && string.Equals(value, "null", StringComparison.OrdinalIgnoreCase)) { return(null); } if (targettype == typeof(TimeSpan)) { return(ParseUtil.ParseDuration(ExpandEnvironmentVariables(value))); } if (targettype == typeof(int) || targettype == typeof(uint) || targettype == typeof(long) || targettype == typeof(ulong)) { return(Convert.ChangeType(ParseUtil.ParseSize(ExpandEnvironmentVariables(value)), targettype)); } if (targettype == typeof(bool)) { return(ParseUtil.ParseBool(ExpandEnvironmentVariables(value))); } return(Convert.ChangeType(ExpandEnvironmentVariables(value), targettype)); }
/// <summary> /// Configures the database and sets it up /// </summary> public override void AfterConfigure() { // If we share the database with another queue, share the connection as well lock (_lock) foreach (var m in _modules) { if (m.Value.ConnectionClass == this.ConnectionClass && m.Value.ConnectionString == this.ConnectionString) { m_con = m.Value.m_con; break; } } // Initialize base.AfterConfigure(); // The CLI will help us a little bit if (string.IsNullOrWhiteSpace(this.SelfUrl)) { this.SelfUrl = Environment.GetEnvironmentVariable("CEEN_SELF_HTTPS_URL"); } if (string.IsNullOrWhiteSpace(this.SelfUrl)) { this.SelfUrl = Environment.GetEnvironmentVariable("CEEN_SELF_HTTP_URL"); } if (string.IsNullOrWhiteSpace(this.SelfUrl)) { throw new Exception($"The QueueModule needs to know the server url to call back to, please set the {nameof(SelfUrl)} variable"); } // Remove trailing slashes to make it easier to concatenate strings this.SelfUrl = this.SelfUrl.Trim().TrimEnd('/'); if (string.IsNullOrWhiteSpace(Name)) { throw new Exception("The name of the queue cannot be empty"); } if (string.IsNullOrWhiteSpace(SecureHeaderName)) { throw new Exception("The secure header name cannot be empty"); } if (string.IsNullOrWhiteSpace(Ratelimit)) { throw new Exception("The rate limit value cannot be empty"); } if (string.IsNullOrWhiteSpace(RetryBackoff)) { throw new Exception("The retry backoff value cannot be empty"); } if (MaxRetries <= 0) { throw new Exception("Invalid max retry count"); } // Assign a random value if (string.IsNullOrWhiteSpace(SecureHeaderValue)) { SecureHeaderValue = Guid.NewGuid().ToString(); } var rl = Ratelimit.Split(new char[] { '/' }, 2); if (rl.Length != 2 || rl[0].Length < 1 || rl[1].Length < 1) { throw new Exception("Unable to parse the ratelimit"); } if (rl[1][0] < '0' || rl[1][0] > '9') { rl[1] = '1' + rl[1]; } m_ratelimitcount = int.Parse(rl[0]); m_ratelimitWindow = ParseUtil.ParseDuration(rl[1]); if (m_ratelimitWindow.Ticks <= 0 || m_ratelimitcount <= 0) { throw new Exception("Invalid rate limit"); } var rb = RetryBackoff.Split(new char[] { ';' }, 3); var re = new System.Text.RegularExpressions.Regex(@"(?<mode>[exp|lin|exponential|linear])\w+(?<rate>.+)"); // Only have the exp/lin, compute start and limit if (rb.Length == 1) { var m = re.Match(rb[0]); if (!m.Success) { throw new Exception("Unable to parse the backoff"); } var duration = m.Groups["rate"].Value; var ps = ParseUtil.ParseDuration(duration); rb = new string[] { duration, rb[0], $"{ps.TotalSeconds * MaxRetries}s" }; } else if (rb.Length == 2) { // First is exp/lin, last is limit, compute start var m = re.Match(rb[0]); if (m.Success) { rb = new string[] { m.Groups["rate"].Value, rb[0], rb[1] }; } // Second is exp/lin, first is start, compute last else { m = re.Match(rb[1]); if (!m.Success) { throw new Exception("Unable to parse the backoff"); } var duration = m.Groups["rate"].Value; var ps = ParseUtil.ParseDuration(duration); rb = new string[] { rb[0], rb[1], $"{ps.TotalSeconds * MaxRetries}s" }; } } var mx = re.Match(rb[1]); if (!mx.Success) { throw new Exception("Unable to parse the backoff"); } m_initialbackoff = ParseUtil.ParseDuration(rb[0]); m_maximumbackoff = ParseUtil.ParseDuration(rb[2]); m_additionalbackoff = ParseUtil.ParseDuration(mx.Groups["rate"].Value); m_exponentialBackoff = mx.Groups["mode"].Value.StartsWith("exp", true, System.Globalization.CultureInfo.InvariantCulture); if (m_initialbackoff.Ticks < 0 || m_maximumbackoff.Ticks < 0 || m_additionalbackoff.Ticks < 0) { throw new Exception("Invalid back-off values"); } m_ratelimiter = new RateLimit(m_ratelimitcount, m_ratelimitWindow); lock (_lock) _modules.Add(Name, this); // Activate the runner SignalRunner(); }