Commit 5e432a94 authored by Savorboard's avatar Savorboard

cleanup code and fix spelling

parent 49f4eb6b
...@@ -20,7 +20,7 @@ namespace Sample.Kafka.SqlServer ...@@ -20,7 +20,7 @@ namespace Sample.Kafka.SqlServer
x.UseDiscovery(d => x.UseDiscovery(d =>
{ {
d.DiscoveryServerHostName = "localhost"; d.DiscoveryServerHostName = "localhost";
d.DiscoveryServerProt = 8500; d.DiscoveryServerPort = 8500;
d.CurrentNodeHostName = "localhost"; d.CurrentNodeHostName = "localhost";
d.CurrentNodePort = 5820; d.CurrentNodePort = 5820;
d.NodeName = "CAP 2号节点"; d.NodeName = "CAP 2号节点";
...@@ -40,8 +40,6 @@ namespace Sample.Kafka.SqlServer ...@@ -40,8 +40,6 @@ namespace Sample.Kafka.SqlServer
app.UseMvc(); app.UseMvc();
app.UseCap(); app.UseCap();
app.UseCapDashboard();
} }
} }
} }
\ No newline at end of file
...@@ -27,7 +27,7 @@ namespace Sample.RabbitMQ.MySql ...@@ -27,7 +27,7 @@ namespace Sample.RabbitMQ.MySql
x.UseDiscovery(d => x.UseDiscovery(d =>
{ {
d.DiscoveryServerHostName = "localhost"; d.DiscoveryServerHostName = "localhost";
d.DiscoveryServerProt = 8500; d.DiscoveryServerPort = 8500;
d.CurrentNodeHostName = "localhost"; d.CurrentNodeHostName = "localhost";
d.CurrentNodePort = 5800; d.CurrentNodePort = 5800;
d.NodeName = "CAP 1号节点"; d.NodeName = "CAP 1号节点";
...@@ -45,7 +45,6 @@ namespace Sample.RabbitMQ.MySql ...@@ -45,7 +45,6 @@ namespace Sample.RabbitMQ.MySql
app.UseMvc(); app.UseMvc();
app.UseCap(); app.UseCap();
app.UseCapDashboard();
} }
} }
} }
...@@ -26,7 +26,7 @@ namespace Sample.RabbitMQ.PostgreSql ...@@ -26,7 +26,7 @@ namespace Sample.RabbitMQ.PostgreSql
x.UseDiscovery(d => x.UseDiscovery(d =>
{ {
d.DiscoveryServerHostName = "localhost"; d.DiscoveryServerHostName = "localhost";
d.DiscoveryServerProt = 8500; d.DiscoveryServerPort = 8500;
d.CurrentNodeHostName = "localhost"; d.CurrentNodeHostName = "localhost";
d.CurrentNodePort = 5800; d.CurrentNodePort = 5800;
d.NodeName = "CAP一号节点"; d.NodeName = "CAP一号节点";
......
...@@ -29,7 +29,7 @@ namespace Sample.RabbitMQ.SqlServer ...@@ -29,7 +29,7 @@ namespace Sample.RabbitMQ.SqlServer
x.UseDiscovery(d => x.UseDiscovery(d =>
{ {
d.DiscoveryServerHostName = "localhost"; d.DiscoveryServerHostName = "localhost";
d.DiscoveryServerProt = 8500; d.DiscoveryServerPort = 8500;
d.CurrentNodeHostName = "localhost"; d.CurrentNodeHostName = "localhost";
d.CurrentNodePort = 5800; d.CurrentNodePort = 5800;
d.NodeName = "CAP一号节点"; d.NodeName = "CAP一号节点";
......
...@@ -10,14 +10,6 @@ namespace DotNetCore.CAP ...@@ -10,14 +10,6 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public class KafkaOptions public class KafkaOptions
{ {
private IEnumerable<KeyValuePair<string, object>> _kafkaConfig;
public KafkaOptions()
{
MainConfig = new Dictionary<string, object>();
}
/// <summary> /// <summary>
/// librdkafka configuration parameters (refer to https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md). /// librdkafka configuration parameters (refer to https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md).
/// <para> /// <para>
...@@ -26,22 +18,28 @@ namespace DotNetCore.CAP ...@@ -26,22 +18,28 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public readonly IDictionary<string, object> MainConfig; public readonly IDictionary<string, object> MainConfig;
private IEnumerable<KeyValuePair<string, object>> _kafkaConfig;
public KafkaOptions()
{
MainConfig = new Dictionary<string, object>();
}
/// <summary> /// <summary>
/// The `bootstrap.servers` item config of <see cref="MainConfig"/>. /// The `bootstrap.servers` item config of <see cref="MainConfig" />.
/// <para> /// <para>
/// Initial list of brokers as a CSV list of broker host or host:port. /// Initial list of brokers as a CSV list of broker host or host:port.
/// </para> /// </para>
/// </summary> /// </summary>
public string Servers { get; set; } public string Servers { get; set; }
internal IEnumerable<KeyValuePair<string, object>> AskafkaConfig() internal IEnumerable<KeyValuePair<string, object>> AsKafkaConfig()
{ {
if (_kafkaConfig == null) if (_kafkaConfig == null)
{ {
if (string.IsNullOrWhiteSpace(Servers)) if (string.IsNullOrWhiteSpace(Servers))
{
throw new ArgumentNullException(nameof(Servers)); throw new ArgumentNullException(nameof(Servers));
}
MainConfig["bootstrap.servers"] = Servers; MainConfig["bootstrap.servers"] = Servers;
MainConfig["queue.buffering.max.ms"] = "10"; MainConfig["queue.buffering.max.ms"] = "10";
......
...@@ -9,18 +9,17 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -9,18 +9,17 @@ namespace Microsoft.Extensions.DependencyInjection
/// <summary> /// <summary>
/// Configuration to use kafka in CAP. /// Configuration to use kafka in CAP.
/// </summary> /// </summary>
/// <param name="options">CAP configuration options</param>
/// <param name="bootstrapServers">Kafka bootstrap server urls.</param> /// <param name="bootstrapServers">Kafka bootstrap server urls.</param>
public static CapOptions UseKafka(this CapOptions options, string bootstrapServers) public static CapOptions UseKafka(this CapOptions options, string bootstrapServers)
{ {
return options.UseKafka(opt => return options.UseKafka(opt => { opt.Servers = bootstrapServers; });
{
opt.Servers = bootstrapServers;
});
} }
/// <summary> /// <summary>
/// Configuration to use kafka in CAP. /// Configuration to use kafka in CAP.
/// </summary> /// </summary>
/// <param name="options">CAP configuration options</param>
/// <param name="configure">Provides programmatic configuration for the kafka .</param> /// <param name="configure">Provides programmatic configuration for the kafka .</param>
/// <returns></returns> /// <returns></returns>
public static CapOptions UseKafka(this CapOptions options, Action<KafkaOptions> configure) public static CapOptions UseKafka(this CapOptions options, Action<KafkaOptions> configure)
......
...@@ -13,12 +13,6 @@ namespace DotNetCore.CAP.Kafka ...@@ -13,12 +13,6 @@ namespace DotNetCore.CAP.Kafka
private readonly KafkaOptions _kafkaOptions; private readonly KafkaOptions _kafkaOptions;
private Consumer<Null, string> _consumerClient; private Consumer<Null, string> _consumerClient;
public event EventHandler<MessageContext> OnMessageReceieved;
public event EventHandler<string> OnError;
public IDeserializer<string> StringDeserializer { get; set; }
public KafkaConsumerClient(string groupId, KafkaOptions options) public KafkaConsumerClient(string groupId, KafkaOptions options)
{ {
_groupId = groupId; _groupId = groupId;
...@@ -26,15 +20,19 @@ namespace DotNetCore.CAP.Kafka ...@@ -26,15 +20,19 @@ namespace DotNetCore.CAP.Kafka
StringDeserializer = new StringDeserializer(Encoding.UTF8); StringDeserializer = new StringDeserializer(Encoding.UTF8);
} }
public IDeserializer<string> StringDeserializer { get; set; }
public event EventHandler<MessageContext> OnMessageReceived;
public event EventHandler<string> OnError;
public void Subscribe(IEnumerable<string> topics) public void Subscribe(IEnumerable<string> topics)
{ {
if (topics == null) if (topics == null)
throw new ArgumentNullException(nameof(topics)); throw new ArgumentNullException(nameof(topics));
if (_consumerClient == null) if (_consumerClient == null)
{
InitKafkaClient(); InitKafkaClient();
}
//_consumerClient.Assign(topics.Select(x=> new TopicPartition(x, 0))); //_consumerClient.Assign(topics.Select(x=> new TopicPartition(x, 0)));
_consumerClient.Subscribe(topics); _consumerClient.Subscribe(topics);
...@@ -65,7 +63,7 @@ namespace DotNetCore.CAP.Kafka ...@@ -65,7 +63,7 @@ namespace DotNetCore.CAP.Kafka
{ {
_kafkaOptions.MainConfig["group.id"] = _groupId; _kafkaOptions.MainConfig["group.id"] = _groupId;
var config = _kafkaOptions.AskafkaConfig(); var config = _kafkaOptions.AsKafkaConfig();
_consumerClient = new Consumer<Null, string>(config, null, StringDeserializer); _consumerClient = new Consumer<Null, string>(config, null, StringDeserializer);
_consumerClient.OnMessage += ConsumerClient_OnMessage; _consumerClient.OnMessage += ConsumerClient_OnMessage;
...@@ -81,7 +79,7 @@ namespace DotNetCore.CAP.Kafka ...@@ -81,7 +79,7 @@ namespace DotNetCore.CAP.Kafka
Content = e.Value Content = e.Value
}; };
OnMessageReceieved?.Invoke(sender, message); OnMessageReceived?.Invoke(sender, message);
} }
private void ConsumerClient_OnError(object sender, Error e) private void ConsumerClient_OnError(object sender, Error e)
......
...@@ -9,8 +9,8 @@ namespace DotNetCore.CAP.Kafka ...@@ -9,8 +9,8 @@ namespace DotNetCore.CAP.Kafka
{ {
internal class PublishQueueExecutor : BasePublishQueueExecutor internal class PublishQueueExecutor : BasePublishQueueExecutor
{ {
private readonly ILogger _logger;
private readonly KafkaOptions _kafkaOptions; private readonly KafkaOptions _kafkaOptions;
private readonly ILogger _logger;
public PublishQueueExecutor( public PublishQueueExecutor(
CapOptions options, CapOptions options,
...@@ -27,7 +27,7 @@ namespace DotNetCore.CAP.Kafka ...@@ -27,7 +27,7 @@ namespace DotNetCore.CAP.Kafka
{ {
try try
{ {
var config = _kafkaOptions.AskafkaConfig(); var config = _kafkaOptions.AsKafkaConfig();
var contentBytes = Encoding.UTF8.GetBytes(content); var contentBytes = Encoding.UTF8.GetBytes(content);
using (var producer = new Producer(config)) using (var producer = new Producer(config))
{ {
...@@ -39,8 +39,6 @@ namespace DotNetCore.CAP.Kafka ...@@ -39,8 +39,6 @@ namespace DotNetCore.CAP.Kafka
return Task.FromResult(OperateResult.Success); return Task.FromResult(OperateResult.Success);
} }
else
{
return Task.FromResult(OperateResult.Failed(new OperateError return Task.FromResult(OperateResult.Failed(new OperateError
{ {
Code = message.Error.Code.ToString(), Code = message.Error.Code.ToString(),
...@@ -48,10 +46,10 @@ namespace DotNetCore.CAP.Kafka ...@@ -48,10 +46,10 @@ namespace DotNetCore.CAP.Kafka
})); }));
} }
} }
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError($"kafka topic message [{keyName}] has benn raised an exception of sending. the exception is: {ex.Message}"); _logger.LogError(
$"kafka topic message [{keyName}] has benn raised an exception of sending. the exception is: {ex.Message}");
return Task.FromResult(OperateResult.Failed(ex)); return Task.FromResult(OperateResult.Failed(ex));
} }
......
...@@ -6,7 +6,7 @@ namespace DotNetCore.CAP ...@@ -6,7 +6,7 @@ namespace DotNetCore.CAP
public class EFOptions public class EFOptions
{ {
/// <summary> /// <summary>
/// EF dbcontext type. /// EF db context type.
/// </summary> /// </summary>
internal Type DbContextType { get; set; } internal Type DbContextType { get; set; }
} }
......
...@@ -29,22 +29,18 @@ namespace DotNetCore.CAP ...@@ -29,22 +29,18 @@ namespace DotNetCore.CAP
_configure(mysqlOptions); _configure(mysqlOptions);
if (mysqlOptions.DbContextType != null) if (mysqlOptions.DbContextType != null)
{
services.AddSingleton(x => services.AddSingleton(x =>
{ {
using (var scope = x.CreateScope()) using (var scope = x.CreateScope())
{ {
var provider = scope.ServiceProvider; var provider = scope.ServiceProvider;
var dbContext = (DbContext)provider.GetService(mysqlOptions.DbContextType); var dbContext = (DbContext) provider.GetService(mysqlOptions.DbContextType);
mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; mysqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return mysqlOptions; return mysqlOptions;
} }
}); });
}
else else
{
services.AddSingleton(mysqlOptions); services.AddSingleton(mysqlOptions);
} }
} }
}
} }
\ No newline at end of file
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class MySqlOptions : EFOptions public class MySqlOptions : EFOptions
......
...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static CapOptions UseMySql(this CapOptions options, string connectionString) public static CapOptions UseMySql(this CapOptions options, string connectionString)
{ {
return options.UseMySql(opt => return options.UseMySql(opt => { opt.ConnectionString = connectionString; });
{
opt.ConnectionString = connectionString;
});
} }
public static CapOptions UseMySql(this CapOptions options, Action<MySqlOptions> configure) public static CapOptions UseMySql(this CapOptions options, Action<MySqlOptions> configure)
...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static CapOptions UseEntityFramework<TContext>(this CapOptions options) public static CapOptions UseEntityFramework<TContext>(this CapOptions options)
where TContext : DbContext where TContext : DbContext
{ {
return options.UseEntityFramework<TContext>(opt => return options.UseEntityFramework<TContext>(opt => { opt.DbContextType = typeof(TContext); });
{
opt.DbContextType = typeof(TContext);
});
} }
public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure) public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure)
...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
if (configure == null) throw new ArgumentNullException(nameof(configure)); if (configure == null) throw new ArgumentNullException(nameof(configure));
var efOptions = new EFOptions { DbContextType = typeof(TContext) }; var efOptions = new EFOptions {DbContextType = typeof(TContext)};
configure(efOptions); configure(efOptions);
options.RegisterExtension(new MySqlCapOptionsExtension(configure)); options.RegisterExtension(new MySqlCapOptionsExtension(configure));
......
...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.MySql ...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.MySql
{ {
public class CapPublisher : CapPublisherBase, ICallbackPublisher public class CapPublisher : CapPublisherBase, ICallbackPublisher
{ {
private readonly DbContext _dbContext;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly MySqlOptions _options; private readonly MySqlOptions _options;
private readonly DbContext _dbContext;
public CapPublisher(IServiceProvider provider, public CapPublisher(IServiceProvider provider,
ILogger<CapPublisher> logger, ILogger<CapPublisher> logger,
...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.MySql ...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.MySql
if (_options.DbContextType != null) if (_options.DbContextType != null)
{ {
IsUsingEF = true; IsUsingEF = true;
_dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType);
}
}
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new MySqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
} }
} }
...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.MySql ...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.MySql
dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted);
dbTrans = dbContextTransaction.GetDbTransaction(); dbTrans = dbContextTransaction.GetDbTransaction();
} }
DbTranasaction = dbTrans; DbTransaction = dbTrans;
} }
protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
dbConnection.Execute(PrepareSql(), message, dbTransaction); dbConnection.Execute(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new MySqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
}
}
#region private methods #region private methods
private string PrepareSql() private string PrepareSql()
{ {
return $"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; return
$"INSERT INTO `{_options.TableNamePrefix}.published` (`Name`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)";
} }
#endregion private methods #endregion private methods
......
...@@ -9,11 +9,10 @@ namespace DotNetCore.CAP.MySql ...@@ -9,11 +9,10 @@ namespace DotNetCore.CAP.MySql
{ {
internal class DefaultAdditionalProcessor : IAdditionalProcessor internal class DefaultAdditionalProcessor : IAdditionalProcessor
{ {
private readonly ILogger _logger;
private readonly MySqlOptions _options;
private const int MaxBatch = 1000; private const int MaxBatch = 1000;
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1); private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly ILogger _logger;
private readonly MySqlOptions _options;
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5); private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public DefaultAdditionalProcessor(ILogger<DefaultAdditionalProcessor> logger, public DefaultAdditionalProcessor(ILogger<DefaultAdditionalProcessor> logger,
...@@ -27,7 +26,8 @@ namespace DotNetCore.CAP.MySql ...@@ -27,7 +26,8 @@ namespace DotNetCore.CAP.MySql
{ {
_logger.LogDebug("Collecting expired entities."); _logger.LogDebug("Collecting expired entities.");
var tables = new[]{ var tables = new[]
{
$"{_options.TableNamePrefix}.published", $"{_options.TableNamePrefix}.published",
$"{_options.TableNamePrefix}.received" $"{_options.TableNamePrefix}.received"
}; };
...@@ -39,8 +39,9 @@ namespace DotNetCore.CAP.MySql ...@@ -39,8 +39,9 @@ namespace DotNetCore.CAP.MySql
{ {
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(_options.ConnectionString))
{ {
removedCount = await connection.ExecuteAsync($@"DELETE FROM `{table}` WHERE ExpiresAt < @now limit @count;", removedCount = await connection.ExecuteAsync(
new { now = DateTime.Now, count = MaxBatch }); $@"DELETE FROM `{table}` WHERE ExpiresAt < @now limit @count;",
new {now = DateTime.Now, count = MaxBatch});
} }
if (removedCount != 0) if (removedCount != 0)
......
...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.MySql ...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.MySql
{ {
public class MySqlFetchedMessage : IFetchedMessage public class MySqlFetchedMessage : IFetchedMessage
{ {
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private readonly Timer _timer;
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1);
private readonly IDbConnection _connection;
private readonly object _lockObject = new object(); private readonly object _lockObject = new object();
private readonly Timer _timer;
private readonly IDbTransaction _transaction;
public MySqlFetchedMessage(int messageId, public MySqlFetchedMessage(int messageId,
MessageType type, MessageType type,
......
...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.MySql ...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.MySql
{ {
public class MySqlStorage : IStorage public class MySqlStorage : IStorage
{ {
private readonly MySqlOptions _options;
private readonly ILogger _logger;
private readonly IDbConnection _existingConnection = null; private readonly IDbConnection _existingConnection = null;
private readonly ILogger _logger;
private readonly MySqlOptions _options;
public MySqlStorage(ILogger<MySqlStorage> logger, MySqlOptions options) public MySqlStorage(ILogger<MySqlStorage> logger, MySqlOptions options)
{ {
...@@ -97,9 +97,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( ...@@ -97,9 +97,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` (
var connection = _existingConnection ?? new MySqlConnection(_options.ConnectionString); var connection = _existingConnection ?? new MySqlConnection(_options.ConnectionString);
if (connection.State == ConnectionState.Closed) if (connection.State == ConnectionState.Closed)
{
connection.Open(); connection.Open();
}
return connection; return connection;
} }
...@@ -112,9 +110,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` ( ...@@ -112,9 +110,7 @@ CREATE TABLE IF NOT EXISTS `{prefix}.published` (
internal void ReleaseConnection(IDbConnection connection) internal void ReleaseConnection(IDbConnection connection)
{ {
if (connection != null && !IsExistingConnection(connection)) if (connection != null && !IsExistingConnection(connection))
{
connection.Dispose(); connection.Dispose();
} }
} }
}
} }
\ No newline at end of file
...@@ -5,23 +5,21 @@ using System.Threading.Tasks; ...@@ -5,23 +5,21 @@ using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor.States;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace DotNetCore.CAP.MySql namespace DotNetCore.CAP.MySql
{ {
public class MySqlStorageConnection : IStorageConnection public class MySqlStorageConnection : IStorageConnection
{ {
private readonly MySqlOptions _options;
private readonly string _prefix; private readonly string _prefix;
public MySqlStorageConnection(MySqlOptions options) public MySqlStorageConnection(MySqlOptions options)
{ {
_options = options; Options = options;
_prefix = _options.TableNamePrefix; _prefix = Options.TableNamePrefix;
} }
public MySqlOptions Options => _options; public MySqlOptions Options { get; }
public IStorageTransaction CreateTransaction() public IStorageTransaction CreateTransaction()
{ {
...@@ -32,7 +30,7 @@ namespace DotNetCore.CAP.MySql ...@@ -32,7 +30,7 @@ namespace DotNetCore.CAP.MySql
{ {
var sql = $@"SELECT * FROM `{_prefix}.published` WHERE `Id`={id};"; var sql = $@"SELECT * FROM `{_prefix}.published` WHERE `Id`={id};";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql); return await connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql);
} }
...@@ -59,7 +57,7 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;"; ...@@ -59,7 +57,7 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;";
{ {
var sql = $"SELECT * FROM `{_prefix}.published` WHERE `StatusName` = '{StatusName.Scheduled}' LIMIT 1;"; var sql = $"SELECT * FROM `{_prefix}.published` WHERE `StatusName` = '{StatusName.Scheduled}' LIMIT 1;";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql); return await connection.QueryFirstOrDefaultAsync<CapPublishedMessage>(sql);
} }
...@@ -69,14 +67,12 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;"; ...@@ -69,14 +67,12 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;";
{ {
var sql = $"SELECT * FROM `{_prefix}.published` WHERE `StatusName` = '{StatusName.Failed}';"; var sql = $"SELECT * FROM `{_prefix}.published` WHERE `StatusName` = '{StatusName.Failed}';";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryAsync<CapPublishedMessage>(sql); return await connection.QueryAsync<CapPublishedMessage>(sql);
} }
} }
// CapReceviedMessage
public async Task StoreReceivedMessageAsync(CapReceivedMessage message) public async Task StoreReceivedMessageAsync(CapReceivedMessage message)
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
...@@ -85,7 +81,7 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;"; ...@@ -85,7 +81,7 @@ DELETE FROM `{_prefix}.queue` LIMIT 1;";
INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`) INSERT INTO `{_prefix}.received`(`Name`,`Group`,`Content`,`Retries`,`Added`,`ExpiresAt`,`StatusName`)
VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
await connection.ExecuteAsync(sql, message); await connection.ExecuteAsync(sql, message);
} }
...@@ -94,25 +90,25 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; ...@@ -94,25 +90,25 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
public async Task<CapReceivedMessage> GetReceivedMessageAsync(int id) public async Task<CapReceivedMessage> GetReceivedMessageAsync(int id)
{ {
var sql = $@"SELECT * FROM `{_prefix}.received` WHERE Id={id};"; var sql = $@"SELECT * FROM `{_prefix}.received` WHERE Id={id};";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryFirstOrDefaultAsync<CapReceivedMessage>(sql); return await connection.QueryFirstOrDefaultAsync<CapReceivedMessage>(sql);
} }
} }
public async Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync() public async Task<CapReceivedMessage> GetNextReceivedMessageToBeEnqueuedAsync()
{ {
var sql = $"SELECT * FROM `{_prefix}.received` WHERE `StatusName` = '{StatusName.Scheduled}' LIMIT 1;"; var sql = $"SELECT * FROM `{_prefix}.received` WHERE `StatusName` = '{StatusName.Scheduled}' LIMIT 1;";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryFirstOrDefaultAsync<CapReceivedMessage>(sql); return await connection.QueryFirstOrDefaultAsync<CapReceivedMessage>(sql);
} }
} }
public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceviedMessages() public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceivedMessages()
{ {
var sql = $"SELECT * FROM `{_prefix}.received` WHERE `StatusName` = '{StatusName.Failed}';"; var sql = $"SELECT * FROM `{_prefix}.received` WHERE `StatusName` = '{StatusName.Failed}';";
using (var connection = new MySqlConnection(_options.ConnectionString)) using (var connection = new MySqlConnection(Options.ConnectionString))
{ {
return await connection.QueryAsync<CapReceivedMessage>(sql); return await connection.QueryAsync<CapReceivedMessage>(sql);
} }
...@@ -123,10 +119,32 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; ...@@ -123,10 +119,32 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
{ {
} }
public bool ChangePublishedState(int messageId, string state)
{
var sql =
$"UPDATE `{_prefix}.published` SET `Retries`=`Retries`+1,`StatusName` = '{state}' WHERE `Id`={messageId}";
using (var connection = new MySqlConnection(Options.ConnectionString))
{
return connection.Execute(sql) > 0;
}
}
public bool ChangeReceivedState(int messageId, string state)
{
var sql =
$"UPDATE `{_prefix}.received` SET `Retries`=`Retries`+1,`StatusName` = '{state}' WHERE `Id`={messageId}";
using (var connection = new MySqlConnection(Options.ConnectionString))
{
return connection.Execute(sql) > 0;
}
}
private async Task<IFetchedMessage> FetchNextMessageCoreAsync(string sql, object args = null) private async Task<IFetchedMessage> FetchNextMessageCoreAsync(string sql, object args = null)
{ {
//here don't use `using` to dispose //here don't use `using` to dispose
var connection = new MySqlConnection(_options.ConnectionString); var connection = new MySqlConnection(Options.ConnectionString);
await connection.OpenAsync(); await connection.OpenAsync();
var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted); var transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
FetchedMessage fetchedMessage; FetchedMessage fetchedMessage;
...@@ -148,17 +166,8 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; ...@@ -148,17 +166,8 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
return null; return null;
} }
return new MySqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection, transaction); return new MySqlFetchedMessage(fetchedMessage.MessageId, fetchedMessage.MessageType, connection,
} transaction);
public bool ChangePublishedState(int messageId, string state)
{
throw new NotImplementedException();
}
public bool ChangeReceivedState(int messageId, string state)
{
throw new NotImplementedException();
} }
} }
} }
\ No newline at end of file
...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.MySql ...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.MySql
{ {
public class MySqlStorageTransaction : IStorageTransaction public class MySqlStorageTransaction : IStorageTransaction
{ {
private readonly string _prefix; private readonly IDbConnection _dbConnection;
private readonly IDbTransaction _dbTransaction; private readonly IDbTransaction _dbTransaction;
private readonly IDbConnection _dbConnection; private readonly string _prefix;
public MySqlStorageTransaction(MySqlStorageConnection connection) public MySqlStorageTransaction(MySqlStorageConnection connection)
{ {
...@@ -28,7 +28,8 @@ namespace DotNetCore.CAP.MySql ...@@ -28,7 +28,8 @@ namespace DotNetCore.CAP.MySql
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"UPDATE `{_prefix}.published` SET `Retries` = @Retries,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; var sql =
$"UPDATE `{_prefix}.published` SET `Retries` = @Retries,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -36,7 +37,8 @@ namespace DotNetCore.CAP.MySql ...@@ -36,7 +37,8 @@ namespace DotNetCore.CAP.MySql
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"UPDATE `{_prefix}.received` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;"; var sql =
$"UPDATE `{_prefix}.received` SET `Retries` = @Retries,`Content`= @Content,`ExpiresAt` = @ExpiresAt,`StatusName`=@StatusName WHERE `Id`=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -45,7 +47,8 @@ namespace DotNetCore.CAP.MySql ...@@ -45,7 +47,8 @@ namespace DotNetCore.CAP.MySql
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"INSERT INTO `{_prefix}.queue` values(@MessageId,@MessageType);"; var sql = $"INSERT INTO `{_prefix}.queue` values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Publish }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish},
_dbTransaction);
} }
public void EnqueueMessage(CapReceivedMessage message) public void EnqueueMessage(CapReceivedMessage message)
...@@ -53,7 +56,8 @@ namespace DotNetCore.CAP.MySql ...@@ -53,7 +56,8 @@ namespace DotNetCore.CAP.MySql
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"INSERT INTO `{_prefix}.queue` values(@MessageId,@MessageType);"; var sql = $"INSERT INTO `{_prefix}.queue` values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Subscribe }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe},
_dbTransaction);
} }
public Task CommitAsync() public Task CommitAsync()
......
...@@ -9,7 +9,7 @@ namespace DotNetCore.CAP ...@@ -9,7 +9,7 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Gets or sets the schema to use when creating database objects. /// Gets or sets the schema to use when creating database objects.
/// Default is <see cref="DefaultSchema"/>. /// Default is <see cref="DefaultSchema" />.
/// </summary> /// </summary>
public string Schema { get; set; } = DefaultSchema; public string Schema { get; set; } = DefaultSchema;
......
...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static CapOptions UsePostgreSql(this CapOptions options, string connectionString) public static CapOptions UsePostgreSql(this CapOptions options, string connectionString)
{ {
return options.UsePostgreSql(opt => return options.UsePostgreSql(opt => { opt.ConnectionString = connectionString; });
{
opt.ConnectionString = connectionString;
});
} }
public static CapOptions UsePostgreSql(this CapOptions options, Action<PostgreSqlOptions> configure) public static CapOptions UsePostgreSql(this CapOptions options, Action<PostgreSqlOptions> configure)
...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static CapOptions UseEntityFramework<TContext>(this CapOptions options) public static CapOptions UseEntityFramework<TContext>(this CapOptions options)
where TContext : DbContext where TContext : DbContext
{ {
return options.UseEntityFramework<TContext>(opt => return options.UseEntityFramework<TContext>(opt => { opt.DbContextType = typeof(TContext); });
{
opt.DbContextType = typeof(TContext);
});
} }
public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure) public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure)
...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
if (configure == null) throw new ArgumentNullException(nameof(configure)); if (configure == null) throw new ArgumentNullException(nameof(configure));
var efOptions = new EFOptions { DbContextType = typeof(TContext) }; var efOptions = new EFOptions {DbContextType = typeof(TContext)};
configure(efOptions); configure(efOptions);
options.RegisterExtension(new PostgreSqlCapOptionsExtension(configure)); options.RegisterExtension(new PostgreSqlCapOptionsExtension(configure));
......
...@@ -29,22 +29,18 @@ namespace DotNetCore.CAP ...@@ -29,22 +29,18 @@ namespace DotNetCore.CAP
_configure(postgreSqlOptions); _configure(postgreSqlOptions);
if (postgreSqlOptions.DbContextType != null) if (postgreSqlOptions.DbContextType != null)
{
services.AddSingleton(x => services.AddSingleton(x =>
{ {
using (var scope = x.CreateScope()) using (var scope = x.CreateScope())
{ {
var provider = scope.ServiceProvider; var provider = scope.ServiceProvider;
var dbContext = (DbContext)provider.GetService(postgreSqlOptions.DbContextType); var dbContext = (DbContext) provider.GetService(postgreSqlOptions.DbContextType);
postgreSqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; postgreSqlOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return postgreSqlOptions; return postgreSqlOptions;
} }
}); });
}
else else
{
services.AddSingleton(postgreSqlOptions); services.AddSingleton(postgreSqlOptions);
} }
} }
}
} }
\ No newline at end of file
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class PostgreSqlOptions : EFOptions public class PostgreSqlOptions : EFOptions
......
...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.PostgreSql
{ {
public class CapPublisher : CapPublisherBase, ICallbackPublisher public class CapPublisher : CapPublisherBase, ICallbackPublisher
{ {
private readonly DbContext _dbContext;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly PostgreSqlOptions _options; private readonly PostgreSqlOptions _options;
private readonly DbContext _dbContext;
public CapPublisher(IServiceProvider provider, public CapPublisher(IServiceProvider provider,
ILogger<CapPublisher> logger, ILogger<CapPublisher> logger,
...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.PostgreSql
if (_options.DbContextType != null) if (_options.DbContextType != null)
{ {
IsUsingEF = true; IsUsingEF = true;
_dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType);
}
}
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new NpgsqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
} }
} }
...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.PostgreSql
dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted);
dbTrans = dbContextTransaction.GetDbTransaction(); dbTrans = dbContextTransaction.GetDbTransaction();
} }
DbTranasaction = dbTrans; DbTransaction = dbTrans;
} }
protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
dbConnection.Execute(PrepareSql(), message, dbTransaction); dbConnection.Execute(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new NpgsqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
}
}
#region private methods #region private methods
private string PrepareSql() private string PrepareSql()
{ {
return $"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; return
$"INSERT INTO \"{_options.Schema}\".\"published\" (\"Name\",\"Content\",\"Retries\",\"Added\",\"ExpiresAt\",\"StatusName\")VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)";
} }
#endregion private methods #endregion private methods
......
...@@ -9,26 +9,22 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -9,26 +9,22 @@ namespace DotNetCore.CAP.PostgreSql
{ {
internal class DefaultAdditionalProcessor : IAdditionalProcessor internal class DefaultAdditionalProcessor : IAdditionalProcessor
{ {
private readonly IServiceProvider _provider;
private readonly ILogger _logger;
private readonly PostgreSqlOptions _options;
private const int MaxBatch = 1000; private const int MaxBatch = 1000;
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
private static readonly string[] Tables = private static readonly string[] Tables =
{ {
"published","received" "published", "received"
}; };
public DefaultAdditionalProcessor( private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
IServiceProvider provider, private readonly ILogger _logger;
ILogger<DefaultAdditionalProcessor> logger, private readonly PostgreSqlOptions _options;
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public DefaultAdditionalProcessor(ILogger<DefaultAdditionalProcessor> logger,
PostgreSqlOptions sqlServerOptions) PostgreSqlOptions sqlServerOptions)
{ {
_logger = logger; _logger = logger;
_provider = provider;
_options = sqlServerOptions; _options = sqlServerOptions;
} }
...@@ -43,8 +39,9 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -43,8 +39,9 @@ namespace DotNetCore.CAP.PostgreSql
{ {
using (var connection = new NpgsqlConnection(_options.ConnectionString)) using (var connection = new NpgsqlConnection(_options.ConnectionString))
{ {
removedCount = await connection.ExecuteAsync($"DELETE FROM \"{_options.Schema}\".\"{table}\" WHERE \"ExpiresAt\" < @now AND \"Id\" IN (SELECT \"Id\" FROM \"{_options.Schema}\".\"{table}\" LIMIT @count);", removedCount = await connection.ExecuteAsync(
new { now = DateTime.Now, count = MaxBatch }); $"DELETE FROM \"{_options.Schema}\".\"{table}\" WHERE \"ExpiresAt\" < @now AND \"Id\" IN (SELECT \"Id\" FROM \"{_options.Schema}\".\"{table}\" LIMIT @count);",
new {now = DateTime.Now, count = MaxBatch});
} }
if (removedCount != 0) if (removedCount != 0)
......
...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.PostgreSql
{ {
public class PostgreSqlFetchedMessage : IFetchedMessage public class PostgreSqlFetchedMessage : IFetchedMessage
{ {
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private readonly Timer _timer;
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1);
private readonly IDbConnection _connection;
private readonly object _lockObject = new object(); private readonly object _lockObject = new object();
private readonly Timer _timer;
private readonly IDbTransaction _transaction;
public PostgreSqlFetchedMessage(int messageId, public PostgreSqlFetchedMessage(int messageId,
MessageType type, MessageType type,
......
...@@ -10,10 +10,10 @@ using DotNetCore.CAP.Models; ...@@ -10,10 +10,10 @@ using DotNetCore.CAP.Models;
namespace DotNetCore.CAP.PostgreSql namespace DotNetCore.CAP.PostgreSql
{ {
public class PostgreSqlMonitoringApi: IMonitoringApi public class PostgreSqlMonitoringApi : IMonitoringApi
{ {
private readonly PostgreSqlStorage _storage;
private readonly PostgreSqlOptions _options; private readonly PostgreSqlOptions _options;
private readonly PostgreSqlStorage _storage;
public PostgreSqlMonitoringApi(IStorage storage, PostgreSqlOptions options) public PostgreSqlMonitoringApi(IStorage storage, PostgreSqlOptions options)
{ {
...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP.PostgreSql
public StatisticsDto GetStatistics() public StatisticsDto GetStatistics()
{ {
string sql = String.Format(@" var sql = string.Format(@"
select count(Id) from ""{0}"".""published"" where ""StatusName"" = N'Succeeded'; select count(Id) from ""{0}"".""published"" where ""StatusName"" = N'Succeeded';
select count(Id) from ""{0}"".""received"" where ""StatusName"" = N'Succeeded'; select count(Id) from ""{0}"".""received"" where ""StatusName"" = N'Succeeded';
select count(Id) from ""{0}"".""published"" where ""StatusName"" = N'Failed'; select count(Id) from ""{0}"".""published"" where ""StatusName"" = N'Failed';
...@@ -57,30 +57,20 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin ...@@ -57,30 +57,20 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin
var where = string.Empty; var where = string.Empty;
if (!string.IsNullOrEmpty(queryDto.StatusName)) if (!string.IsNullOrEmpty(queryDto.StatusName))
{ if (string.Equals(queryDto.StatusName, StatusName.Processing,
if (string.Equals(queryDto.StatusName, StatusName.Processing, StringComparison.CurrentCultureIgnoreCase)) StringComparison.CurrentCultureIgnoreCase))
{
where += " and \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')"; where += " and \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')";
}
else else
{
where += " and \"StatusName\" = @StatusName"; where += " and \"StatusName\" = @StatusName";
}
}
if (!string.IsNullOrEmpty(queryDto.Name)) if (!string.IsNullOrEmpty(queryDto.Name))
{
where += " and \"Name\" = @Name"; where += " and \"Name\" = @Name";
}
if (!string.IsNullOrEmpty(queryDto.Group)) if (!string.IsNullOrEmpty(queryDto.Group))
{
where += " and \"Group\" = @Group"; where += " and \"Group\" = @Group";
}
if (!string.IsNullOrEmpty(queryDto.Content)) if (!string.IsNullOrEmpty(queryDto.Content))
{
where += " and \"Content\" like '%@Content%'"; where += " and \"Content\" like '%@Content%'";
}
var sqlQuery = $"select * from \"{_options.Schema}\".\"{tableName}\" where 1=1 {where} order by \"Added\" desc offset @Offset limit @Limit"; var sqlQuery =
$"select * from \"{_options.Schema}\".\"{tableName}\" where 1=1 {where} order by \"Added\" desc offset @Offset limit @Limit";
return UseConnection(conn => conn.Query<MessageDto>(sqlQuery, new return UseConnection(conn => conn.Query<MessageDto>(sqlQuery, new
{ {
...@@ -89,7 +79,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin ...@@ -89,7 +79,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin
queryDto.Name, queryDto.Name,
queryDto.Content, queryDto.Content,
Offset = queryDto.CurrentPage * queryDto.PageSize, Offset = queryDto.CurrentPage * queryDto.PageSize,
Limit = queryDto.PageSize, Limit = queryDto.PageSize
}).ToList()); }).ToList());
} }
...@@ -143,7 +133,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin ...@@ -143,7 +133,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin
? $"select count(Id) from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')" ? $"select count(Id) from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" in (N'Processing',N'Scheduled',N'Enqueued')"
: $"select count(Id) from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" = @state"; : $"select count(Id) from \"{_options.Schema}\".\"{tableName}\" where \"StatusName\" = @state";
var count = connection.ExecuteScalar<int>(sqlQuery, new { state = statusName }); var count = connection.ExecuteScalar<int>(sqlQuery, new {state = statusName});
return count; return count;
} }
...@@ -152,7 +142,8 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin ...@@ -152,7 +142,8 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin
return _storage.UseConnection(action); return _storage.UseConnection(action);
} }
private Dictionary<DateTime, int> GetHourlyTimelineStats(IDbConnection connection, string tableName, string statusName) private Dictionary<DateTime, int> GetHourlyTimelineStats(IDbConnection connection, string tableName,
string statusName)
{ {
var endDate = DateTime.Now; var endDate = DateTime.Now;
var dates = new List<DateTime>(); var dates = new List<DateTime>();
...@@ -174,7 +165,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin ...@@ -174,7 +165,7 @@ select count(Id) from ""{0}"".""received"" where ""StatusName"" in (N'Processin
IDictionary<string, DateTime> keyMaps) IDictionary<string, DateTime> keyMaps)
{ {
//SQL Server 2012+ //SQL Server 2012+
string sqlQuery = var sqlQuery =
$@" $@"
with aggr as ( with aggr as (
select to_char(""Added"",'yyyy-MM-dd-HH') as ""Key"", select to_char(""Added"",'yyyy-MM-dd-HH') as ""Key"",
...@@ -187,13 +178,11 @@ select ""Key"",""Count"" from aggr where ""Key"" in @keys;"; ...@@ -187,13 +178,11 @@ select ""Key"",""Count"" from aggr where ""Key"" in @keys;";
var valuesMap = connection.Query( var valuesMap = connection.Query(
sqlQuery, sqlQuery,
new { keys = keyMaps.Keys, statusName }) new {keys = keyMaps.Keys, statusName})
.ToDictionary(x => (string)x.Key, x => (int)x.Count); .ToDictionary(x => (string) x.Key, x => (int) x.Count);
foreach (var key in keyMaps.Keys) foreach (var key in keyMaps.Keys)
{
if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0);
}
var result = new Dictionary<DateTime, int>(); var result = new Dictionary<DateTime, int>();
for (var i = 0; i < keyMaps.Count; i++) for (var i = 0; i < keyMaps.Count; i++)
......
...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.PostgreSql
{ {
public class PostgreSqlStorage : IStorage public class PostgreSqlStorage : IStorage
{ {
private readonly PostgreSqlOptions _options;
private readonly ILogger _logger;
private readonly IDbConnection _existingConnection = null; private readonly IDbConnection _existingConnection = null;
private readonly ILogger _logger;
private readonly PostgreSqlOptions _options;
public PostgreSqlStorage(ILogger<PostgreSqlStorage> logger, PostgreSqlOptions options) public PostgreSqlStorage(ILogger<PostgreSqlStorage> logger, PostgreSqlOptions options)
{ {
...@@ -64,9 +64,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -64,9 +64,7 @@ namespace DotNetCore.CAP.PostgreSql
var connection = _existingConnection ?? new NpgsqlConnection(_options.ConnectionString); var connection = _existingConnection ?? new NpgsqlConnection(_options.ConnectionString);
if (connection.State == ConnectionState.Closed) if (connection.State == ConnectionState.Closed)
{
connection.Open(); connection.Open();
}
return connection; return connection;
} }
...@@ -79,10 +77,8 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -79,10 +77,8 @@ namespace DotNetCore.CAP.PostgreSql
internal void ReleaseConnection(IDbConnection connection) internal void ReleaseConnection(IDbConnection connection)
{ {
if (connection != null && !IsExistingConnection(connection)) if (connection != null && !IsExistingConnection(connection))
{
connection.Dispose(); connection.Dispose();
} }
}
protected virtual string CreateDbTablesScript(string schema) protected virtual string CreateDbTablesScript(string schema)
{ {
......
...@@ -63,8 +63,6 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -63,8 +63,6 @@ namespace DotNetCore.CAP.PostgreSql
} }
} }
// CapReceviedMessage
public async Task StoreReceivedMessageAsync(CapReceivedMessage message) public async Task StoreReceivedMessageAsync(CapReceivedMessage message)
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
...@@ -87,7 +85,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -87,7 +85,7 @@ namespace DotNetCore.CAP.PostgreSql
} }
} }
public async Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync() public async Task<CapReceivedMessage> GetNextReceivedMessageToBeEnqueuedAsync()
{ {
var sql = var sql =
$"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;"; $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\" = '{StatusName.Scheduled}' FOR UPDATE SKIP LOCKED LIMIT 1;";
...@@ -97,7 +95,7 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -97,7 +95,7 @@ namespace DotNetCore.CAP.PostgreSql
} }
} }
public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceviedMessages() public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceivedMessages()
{ {
var sql = var sql =
$"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\"='{StatusName.Failed}' LIMIT 1000;"; $"SELECT * FROM \"{Options.Schema}\".\"received\" WHERE \"StatusName\"='{StatusName.Failed}' LIMIT 1000;";
......
...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.PostgreSql
{ {
public class PostgreSqlStorageTransaction : IStorageTransaction public class PostgreSqlStorageTransaction : IStorageTransaction
{ {
private readonly string _schema; private readonly IDbConnection _dbConnection;
private readonly IDbTransaction _dbTransaction; private readonly IDbTransaction _dbTransaction;
private readonly IDbConnection _dbConnection; private readonly string _schema;
public PostgreSqlStorageTransaction(PostgreSqlStorageConnection connection) public PostgreSqlStorageTransaction(PostgreSqlStorageConnection connection)
{ {
...@@ -28,7 +28,10 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -28,7 +28,10 @@ namespace DotNetCore.CAP.PostgreSql
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $@"UPDATE ""{_schema}"".""published"" SET ""Retries""=@Retries,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; var sql =
$@"UPDATE ""{
_schema
}"".""published"" SET ""Retries""=@Retries,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -36,7 +39,10 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -36,7 +39,10 @@ namespace DotNetCore.CAP.PostgreSql
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $@"UPDATE ""{_schema}"".""received"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;"; var sql =
$@"UPDATE ""{
_schema
}"".""received"" SET ""Retries""=@Retries,""Content""= @Content,""ExpiresAt""=@ExpiresAt,""StatusName""=@StatusName WHERE ""Id""=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -45,7 +51,8 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -45,7 +51,8 @@ namespace DotNetCore.CAP.PostgreSql
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Publish }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish},
_dbTransaction);
} }
public void EnqueueMessage(CapReceivedMessage message) public void EnqueueMessage(CapReceivedMessage message)
...@@ -53,7 +60,8 @@ namespace DotNetCore.CAP.PostgreSql ...@@ -53,7 +60,8 @@ namespace DotNetCore.CAP.PostgreSql
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);"; var sql = $@"INSERT INTO ""{_schema}"".""queue"" values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Subscribe }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe},
_dbTransaction);
} }
public Task CommitAsync() public Task CommitAsync()
......
...@@ -8,10 +8,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -8,10 +8,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static CapOptions UseRabbitMQ(this CapOptions options, string hostName) public static CapOptions UseRabbitMQ(this CapOptions options, string hostName)
{ {
return options.UseRabbitMQ(opt => return options.UseRabbitMQ(opt => { opt.HostName = hostName; });
{
opt.HostName = hostName;
});
} }
public static CapOptions UseRabbitMQ(this CapOptions options, Action<RabbitMQOptions> configure) public static CapOptions UseRabbitMQ(this CapOptions options, Action<RabbitMQOptions> configure)
......
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class RabbitMQOptions public class RabbitMQOptions
......
...@@ -10,13 +10,13 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -10,13 +10,13 @@ namespace DotNetCore.CAP.RabbitMQ
{ {
private const int DefaultPoolSize = 15; private const int DefaultPoolSize = 15;
private readonly ConcurrentQueue<IConnection> _pool = new ConcurrentQueue<IConnection>();
private readonly Func<IConnection> _activator; private readonly Func<IConnection> _activator;
private int _maxSize; private readonly ConcurrentQueue<IConnection> _pool = new ConcurrentQueue<IConnection>();
private int _count; private int _count;
private int _maxSize;
public ConnectionPool(RabbitMQOptions options) public ConnectionPool(RabbitMQOptions options)
{ {
_maxSize = DefaultPoolSize; _maxSize = DefaultPoolSize;
...@@ -24,9 +24,28 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -24,9 +24,28 @@ namespace DotNetCore.CAP.RabbitMQ
_activator = CreateActivator(options); _activator = CreateActivator(options);
} }
IConnection IConnectionPool.Rent()
{
return Rent();
}
bool IConnectionPool.Return(IConnection connection)
{
return Return(connection);
}
public void Dispose()
{
_maxSize = 0;
IConnection context;
while (_pool.TryDequeue(out context))
context.Dispose();
}
private static Func<IConnection> CreateActivator(RabbitMQOptions options) private static Func<IConnection> CreateActivator(RabbitMQOptions options)
{ {
var factory = new ConnectionFactory() var factory = new ConnectionFactory
{ {
HostName = options.HostName, HostName = options.HostName,
UserName = options.UserName, UserName = options.UserName,
...@@ -43,7 +62,7 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -43,7 +62,7 @@ namespace DotNetCore.CAP.RabbitMQ
public virtual IConnection Rent() public virtual IConnection Rent()
{ {
if (_pool.TryDequeue(out IConnection connection)) if (_pool.TryDequeue(out var connection))
{ {
Interlocked.Decrement(ref _count); Interlocked.Decrement(ref _count);
...@@ -72,20 +91,5 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -72,20 +91,5 @@ namespace DotNetCore.CAP.RabbitMQ
return false; return false;
} }
IConnection IConnectionPool.Rent() => Rent();
bool IConnectionPool.Return(IConnection connection) => Return(connection);
public void Dispose()
{
_maxSize = 0;
IConnection context;
while (_pool.TryDequeue(out context))
{
context.Dispose();
}
}
} }
} }
\ No newline at end of file
...@@ -9,8 +9,8 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -9,8 +9,8 @@ namespace DotNetCore.CAP.RabbitMQ
{ {
internal sealed class PublishQueueExecutor : BasePublishQueueExecutor internal sealed class PublishQueueExecutor : BasePublishQueueExecutor
{ {
private readonly ILogger _logger;
private readonly ConnectionPool _connectionPool; private readonly ConnectionPool _connectionPool;
private readonly ILogger _logger;
private readonly RabbitMQOptions _rabbitMQOptions; private readonly RabbitMQOptions _rabbitMQOptions;
public PublishQueueExecutor( public PublishQueueExecutor(
...@@ -36,11 +36,11 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -36,11 +36,11 @@ namespace DotNetCore.CAP.RabbitMQ
{ {
var body = Encoding.UTF8.GetBytes(content); var body = Encoding.UTF8.GetBytes(content);
channel.ExchangeDeclare(_rabbitMQOptions.TopicExchangeName, RabbitMQOptions.ExchangeType, durable: true); channel.ExchangeDeclare(_rabbitMQOptions.TopicExchangeName, RabbitMQOptions.ExchangeType, true);
channel.BasicPublish(exchange: _rabbitMQOptions.TopicExchangeName, channel.BasicPublish(_rabbitMQOptions.TopicExchangeName,
routingKey: keyName, keyName,
basicProperties: null, null,
body: body); body);
_logger.LogDebug($"rabbitmq topic message [{keyName}] has been published."); _logger.LogDebug($"rabbitmq topic message [{keyName}] has been published.");
} }
...@@ -48,10 +48,11 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -48,10 +48,11 @@ namespace DotNetCore.CAP.RabbitMQ
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError($"rabbitmq topic message [{keyName}] has benn raised an exception of sending. the exception is: {ex.Message}"); _logger.LogError(
$"rabbitmq topic message [{keyName}] has benn raised an exception of sending. the exception is: {ex.Message}");
return Task.FromResult(OperateResult.Failed(ex, return Task.FromResult(OperateResult.Failed(ex,
new OperateError() new OperateError
{ {
Code = ex.HResult.ToString(), Code = ex.HResult.ToString(),
Description = ex.Message Description = ex.Message
......
...@@ -10,18 +10,14 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -10,18 +10,14 @@ namespace DotNetCore.CAP.RabbitMQ
{ {
internal sealed class RabbitMQConsumerClient : IConsumerClient internal sealed class RabbitMQConsumerClient : IConsumerClient
{ {
private readonly ConnectionPool _connectionPool;
private readonly string _exchageName; private readonly string _exchageName;
private readonly string _queueName; private readonly string _queueName;
private readonly RabbitMQOptions _rabbitMQOptions; private readonly RabbitMQOptions _rabbitMQOptions;
private readonly ConnectionPool _connectionPool;
private IModel _channel; private IModel _channel;
private ulong _deliveryTag; private ulong _deliveryTag;
public event EventHandler<MessageContext> OnMessageReceieved;
public event EventHandler<string> OnError;
public RabbitMQConsumerClient(string queueName, public RabbitMQConsumerClient(string queueName,
ConnectionPool connectionPool, ConnectionPool connectionPool,
RabbitMQOptions options) RabbitMQOptions options)
...@@ -34,36 +30,17 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -34,36 +30,17 @@ namespace DotNetCore.CAP.RabbitMQ
InitClient(); InitClient();
} }
private void InitClient() public event EventHandler<MessageContext> OnMessageReceived;
{
var connection = _connectionPool.Rent();
_channel = connection.CreateModel(); public event EventHandler<string> OnError;
_channel.ExchangeDeclare(
exchange: _exchageName,
type: RabbitMQOptions.ExchangeType,
durable: true);
var arguments = new Dictionary<string, object> { { "x-message-ttl", _rabbitMQOptions.QueueMessageExpires } };
_channel.QueueDeclare(_queueName,
durable: true,
exclusive: false,
autoDelete: false,
arguments: arguments);
_connectionPool.Return(connection);
}
public void Subscribe(IEnumerable<string> topics) public void Subscribe(IEnumerable<string> topics)
{ {
if (topics == null) throw new ArgumentNullException(nameof(topics)); if (topics == null) throw new ArgumentNullException(nameof(topics));
foreach (var topic in topics) foreach (var topic in topics)
{
_channel.QueueBind(_queueName, _exchageName, topic); _channel.QueueBind(_queueName, _exchageName, topic);
} }
}
public void Listening(TimeSpan timeout, CancellationToken cancellationToken) public void Listening(TimeSpan timeout, CancellationToken cancellationToken)
{ {
...@@ -72,10 +49,8 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -72,10 +49,8 @@ namespace DotNetCore.CAP.RabbitMQ
consumer.Shutdown += OnConsumerShutdown; consumer.Shutdown += OnConsumerShutdown;
_channel.BasicConsume(_queueName, false, consumer); _channel.BasicConsume(_queueName, false, consumer);
while (true) while (true)
{
Task.Delay(timeout, cancellationToken).GetAwaiter().GetResult(); Task.Delay(timeout, cancellationToken).GetAwaiter().GetResult();
} }
}
public void Commit() public void Commit()
{ {
...@@ -87,6 +62,27 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -87,6 +62,27 @@ namespace DotNetCore.CAP.RabbitMQ
_channel.Dispose(); _channel.Dispose();
} }
private void InitClient()
{
var connection = _connectionPool.Rent();
_channel = connection.CreateModel();
_channel.ExchangeDeclare(
_exchageName,
RabbitMQOptions.ExchangeType,
true);
var arguments = new Dictionary<string, object> {{"x-message-ttl", _rabbitMQOptions.QueueMessageExpires}};
_channel.QueueDeclare(_queueName,
true,
false,
false,
arguments);
_connectionPool.Return(connection);
}
private void OnConsumerReceived(object sender, BasicDeliverEventArgs e) private void OnConsumerReceived(object sender, BasicDeliverEventArgs e)
{ {
_deliveryTag = e.DeliveryTag; _deliveryTag = e.DeliveryTag;
...@@ -96,7 +92,7 @@ namespace DotNetCore.CAP.RabbitMQ ...@@ -96,7 +92,7 @@ namespace DotNetCore.CAP.RabbitMQ
Name = e.RoutingKey, Name = e.RoutingKey,
Content = Encoding.UTF8.GetString(e.Body) Content = Encoding.UTF8.GetString(e.Body)
}; };
OnMessageReceieved?.Invoke(sender, message); OnMessageReceived?.Invoke(sender, message);
} }
private void OnConsumerShutdown(object sender, ShutdownEventArgs e) private void OnConsumerShutdown(object sender, ShutdownEventArgs e)
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
{ {
internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory internal sealed class RabbitMQConsumerClientFactory : IConsumerClientFactory
{ {
private readonly RabbitMQOptions _rabbitMQOptions;
private readonly ConnectionPool _connectionPool; private readonly ConnectionPool _connectionPool;
private readonly RabbitMQOptions _rabbitMQOptions;
public RabbitMQConsumerClientFactory(RabbitMQOptions rabbitMQOptions, ConnectionPool pool) public RabbitMQConsumerClientFactory(RabbitMQOptions rabbitMQOptions, ConnectionPool pool)
......
...@@ -9,7 +9,7 @@ namespace DotNetCore.CAP ...@@ -9,7 +9,7 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Gets or sets the schema to use when creating database objects. /// Gets or sets the schema to use when creating database objects.
/// Default is <see cref="DefaultSchema"/>. /// Default is <see cref="DefaultSchema" />.
/// </summary> /// </summary>
public string Schema { get; set; } = DefaultSchema; public string Schema { get; set; } = DefaultSchema;
......
...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -9,10 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
public static CapOptions UseSqlServer(this CapOptions options, string connectionString) public static CapOptions UseSqlServer(this CapOptions options, string connectionString)
{ {
return options.UseSqlServer(opt => return options.UseSqlServer(opt => { opt.ConnectionString = connectionString; });
{
opt.ConnectionString = connectionString;
});
} }
public static CapOptions UseSqlServer(this CapOptions options, Action<SqlServerOptions> configure) public static CapOptions UseSqlServer(this CapOptions options, Action<SqlServerOptions> configure)
...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -27,10 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static CapOptions UseEntityFramework<TContext>(this CapOptions options) public static CapOptions UseEntityFramework<TContext>(this CapOptions options)
where TContext : DbContext where TContext : DbContext
{ {
return options.UseEntityFramework<TContext>(opt => return options.UseEntityFramework<TContext>(opt => { opt.DbContextType = typeof(TContext); });
{
opt.DbContextType = typeof(TContext);
});
} }
public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure) public static CapOptions UseEntityFramework<TContext>(this CapOptions options, Action<EFOptions> configure)
...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -38,7 +32,7 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
if (configure == null) throw new ArgumentNullException(nameof(configure)); if (configure == null) throw new ArgumentNullException(nameof(configure));
var efOptions = new EFOptions { DbContextType = typeof(TContext) }; var efOptions = new EFOptions {DbContextType = typeof(TContext)};
configure(efOptions); configure(efOptions);
options.RegisterExtension(new SqlServerCapOptionsExtension(configure)); options.RegisterExtension(new SqlServerCapOptionsExtension(configure));
......
...@@ -34,22 +34,18 @@ namespace DotNetCore.CAP ...@@ -34,22 +34,18 @@ namespace DotNetCore.CAP
_configure(sqlServerOptions); _configure(sqlServerOptions);
if (sqlServerOptions.DbContextType != null) if (sqlServerOptions.DbContextType != null)
{
services.AddSingleton(x => services.AddSingleton(x =>
{ {
using (var scope = x.CreateScope()) using (var scope = x.CreateScope())
{ {
var provider = scope.ServiceProvider; var provider = scope.ServiceProvider;
var dbContext = (DbContext)provider.GetService(sqlServerOptions.DbContextType); var dbContext = (DbContext) provider.GetService(sqlServerOptions.DbContextType);
sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString; sqlServerOptions.ConnectionString = dbContext.Database.GetDbConnection().ConnectionString;
return sqlServerOptions; return sqlServerOptions;
} }
}); });
}
else else
{
services.AddSingleton(sqlServerOptions); services.AddSingleton(sqlServerOptions);
} }
} }
}
} }
\ No newline at end of file
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class SqlServerOptions : EFOptions public class SqlServerOptions : EFOptions
......
...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.SqlServer ...@@ -13,9 +13,9 @@ namespace DotNetCore.CAP.SqlServer
{ {
public class CapPublisher : CapPublisherBase, ICallbackPublisher public class CapPublisher : CapPublisherBase, ICallbackPublisher
{ {
private readonly DbContext _dbContext;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly SqlServerOptions _options; private readonly SqlServerOptions _options;
private readonly DbContext _dbContext;
public CapPublisher(IServiceProvider provider, public CapPublisher(IServiceProvider provider,
ILogger<CapPublisher> logger, ILogger<CapPublisher> logger,
...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.SqlServer ...@@ -28,7 +28,15 @@ namespace DotNetCore.CAP.SqlServer
if (_options.DbContextType != null) if (_options.DbContextType != null)
{ {
IsUsingEF = true; IsUsingEF = true;
_dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType); _dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType);
}
}
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new SqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
} }
} }
...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.SqlServer ...@@ -45,36 +53,31 @@ namespace DotNetCore.CAP.SqlServer
dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted);
dbTrans = dbContextTransaction.GetDbTransaction(); dbTrans = dbContextTransaction.GetDbTransaction();
} }
DbTranasaction = dbTrans; DbTransaction = dbTrans;
} }
protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
dbConnection.Execute(PrepareSql(), message, dbTransaction); dbConnection.Execute(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message) protected override async Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message)
{ {
await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction); await dbConnection.ExecuteAsync(PrepareSql(), message, dbTransaction);
_logger.LogInformation("Published Message has been persisted in the database. name:" + message); _logger.LogInformation("Published Message has been persisted in the database. name:" + message);
} }
public async Task PublishAsync(CapPublishedMessage message)
{
using (var conn = new SqlConnection(_options.ConnectionString))
{
await conn.ExecuteAsync(PrepareSql(), message);
}
}
#region private methods #region private methods
private string PrepareSql() private string PrepareSql()
{ {
return $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)"; return
$"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)";
} }
#endregion private methods #endregion private methods
......
...@@ -9,18 +9,18 @@ namespace DotNetCore.CAP.SqlServer ...@@ -9,18 +9,18 @@ namespace DotNetCore.CAP.SqlServer
{ {
public class DefaultAdditionalProcessor : IAdditionalProcessor public class DefaultAdditionalProcessor : IAdditionalProcessor
{ {
private readonly ILogger _logger;
private readonly SqlServerOptions _options;
private const int MaxBatch = 1000; private const int MaxBatch = 1000;
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
private static readonly string[] Tables = private static readonly string[] Tables =
{ {
"Published","Received" "Published", "Received"
}; };
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly ILogger _logger;
private readonly SqlServerOptions _options;
private readonly TimeSpan _waitingInterval = TimeSpan.FromMinutes(5);
public DefaultAdditionalProcessor(ILogger<DefaultAdditionalProcessor> logger, public DefaultAdditionalProcessor(ILogger<DefaultAdditionalProcessor> logger,
SqlServerOptions sqlServerOptions) SqlServerOptions sqlServerOptions)
{ {
...@@ -42,7 +42,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -42,7 +42,7 @@ namespace DotNetCore.CAP.SqlServer
removedCount = await connection.ExecuteAsync($@" removedCount = await connection.ExecuteAsync($@"
DELETE TOP (@count) DELETE TOP (@count)
FROM [{_options.Schema}].[{table}] WITH (readpast) FROM [{_options.Schema}].[{table}] WITH (readpast)
WHERE ExpiresAt < @now;", new { now = DateTime.Now, count = MaxBatch }); WHERE ExpiresAt < @now;", new {now = DateTime.Now, count = MaxBatch});
} }
if (removedCount != 0) if (removedCount != 0)
......
...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.SqlServer ...@@ -8,11 +8,11 @@ namespace DotNetCore.CAP.SqlServer
{ {
public class SqlServerFetchedMessage : IFetchedMessage public class SqlServerFetchedMessage : IFetchedMessage
{ {
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private readonly Timer _timer;
private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1); private static readonly TimeSpan KeepAliveInterval = TimeSpan.FromMinutes(1);
private readonly IDbConnection _connection;
private readonly object _lockObject = new object(); private readonly object _lockObject = new object();
private readonly Timer _timer;
private readonly IDbTransaction _transaction;
public SqlServerFetchedMessage(int messageId, public SqlServerFetchedMessage(int messageId,
MessageType type, MessageType type,
......
...@@ -12,8 +12,8 @@ namespace DotNetCore.CAP.SqlServer ...@@ -12,8 +12,8 @@ namespace DotNetCore.CAP.SqlServer
{ {
internal class SqlServerMonitoringApi : IMonitoringApi internal class SqlServerMonitoringApi : IMonitoringApi
{ {
private readonly SqlServerStorage _storage;
private readonly SqlServerOptions _options; private readonly SqlServerOptions _options;
private readonly SqlServerStorage _storage;
public SqlServerMonitoringApi(IStorage storage, SqlServerOptions options) public SqlServerMonitoringApi(IStorage storage, SqlServerOptions options)
{ {
...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP.SqlServer
public StatisticsDto GetStatistics() public StatisticsDto GetStatistics()
{ {
string sql = String.Format(@" var sql = string.Format(@"
set transaction isolation level read committed; set transaction isolation level read committed;
select count(Id) from [{0}].Published with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Published with (nolock) where StatusName = N'Succeeded';
select count(Id) from [{0}].Received with (nolock) where StatusName = N'Succeeded'; select count(Id) from [{0}].Received with (nolock) where StatusName = N'Succeeded';
...@@ -31,7 +31,7 @@ select count(Id) from [{0}].Published with (nolock) where StatusName = N'Failed' ...@@ -31,7 +31,7 @@ select count(Id) from [{0}].Published with (nolock) where StatusName = N'Failed'
select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed'; select count(Id) from [{0}].Received with (nolock) where StatusName = N'Failed';
select count(Id) from [{0}].Published with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued'); select count(Id) from [{0}].Published with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued');
select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued');", select count(Id) from [{0}].Received with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued');",
_options.Schema); _options.Schema);
var statistics = UseConnection(connection => var statistics = UseConnection(connection =>
{ {
...@@ -71,30 +71,20 @@ _options.Schema); ...@@ -71,30 +71,20 @@ _options.Schema);
var tableName = queryDto.MessageType == MessageType.Publish ? "Published" : "Received"; var tableName = queryDto.MessageType == MessageType.Publish ? "Published" : "Received";
var where = string.Empty; var where = string.Empty;
if (!string.IsNullOrEmpty(queryDto.StatusName)) if (!string.IsNullOrEmpty(queryDto.StatusName))
{ if (string.Equals(queryDto.StatusName, StatusName.Processing,
if (string.Equals(queryDto.StatusName, StatusName.Processing, StringComparison.CurrentCultureIgnoreCase)) StringComparison.CurrentCultureIgnoreCase))
{
where += " and statusname in (N'Processing',N'Scheduled',N'Enqueued')"; where += " and statusname in (N'Processing',N'Scheduled',N'Enqueued')";
}
else else
{
where += " and statusname=@StatusName"; where += " and statusname=@StatusName";
}
}
if (!string.IsNullOrEmpty(queryDto.Name)) if (!string.IsNullOrEmpty(queryDto.Name))
{
where += " and name=@Name"; where += " and name=@Name";
}
if (!string.IsNullOrEmpty(queryDto.Group)) if (!string.IsNullOrEmpty(queryDto.Group))
{
where += " and group=@Group"; where += " and group=@Group";
}
if (!string.IsNullOrEmpty(queryDto.Content)) if (!string.IsNullOrEmpty(queryDto.Content))
{
where += " and content like '%@Content%'"; where += " and content like '%@Content%'";
}
var sqlQuery = $"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only"; var sqlQuery =
$"select * from [{_options.Schema}].{tableName} where 1=1 {where} order by Added desc offset @Offset rows fetch next @Limit rows only";
return UseConnection(conn => conn.Query<MessageDto>(sqlQuery, new return UseConnection(conn => conn.Query<MessageDto>(sqlQuery, new
{ {
...@@ -103,7 +93,7 @@ _options.Schema); ...@@ -103,7 +93,7 @@ _options.Schema);
queryDto.Name, queryDto.Name,
queryDto.Content, queryDto.Content,
Offset = queryDto.CurrentPage * queryDto.PageSize, Offset = queryDto.CurrentPage * queryDto.PageSize,
Limit = queryDto.PageSize, Limit = queryDto.PageSize
}).ToList()); }).ToList());
} }
...@@ -143,7 +133,7 @@ _options.Schema); ...@@ -143,7 +133,7 @@ _options.Schema);
? $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued')" ? $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName in (N'Processing',N'Scheduled',N'Enqueued')"
: $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state"; : $"select count(Id) from [{_options.Schema}].{tableName} with (nolock) where StatusName = @state";
var count = connection.ExecuteScalar<int>(sqlQuery, new { state = statusName }); var count = connection.ExecuteScalar<int>(sqlQuery, new {state = statusName});
return count; return count;
} }
...@@ -152,7 +142,8 @@ _options.Schema); ...@@ -152,7 +142,8 @@ _options.Schema);
return _storage.UseConnection(action); return _storage.UseConnection(action);
} }
private Dictionary<DateTime, int> GetHourlyTimelineStats(IDbConnection connection, string tableName, string statusName) private Dictionary<DateTime, int> GetHourlyTimelineStats(IDbConnection connection, string tableName,
string statusName)
{ {
var endDate = DateTime.Now; var endDate = DateTime.Now;
var dates = new List<DateTime>(); var dates = new List<DateTime>();
...@@ -174,8 +165,8 @@ _options.Schema); ...@@ -174,8 +165,8 @@ _options.Schema);
IDictionary<string, DateTime> keyMaps) IDictionary<string, DateTime> keyMaps)
{ {
//SQL Server 2012+ //SQL Server 2012+
string sqlQuery = var sqlQuery =
$@" $@"
with aggr as ( with aggr as (
select FORMAT(Added,'yyyy-MM-dd-HH') as [Key], select FORMAT(Added,'yyyy-MM-dd-HH') as [Key],
count(id) [Count] count(id) [Count]
...@@ -187,13 +178,11 @@ select [Key], [Count] from aggr with (nolock) where [Key] in @keys;"; ...@@ -187,13 +178,11 @@ select [Key], [Count] from aggr with (nolock) where [Key] in @keys;";
var valuesMap = connection.Query( var valuesMap = connection.Query(
sqlQuery, sqlQuery,
new { keys = keyMaps.Keys, statusName }) new {keys = keyMaps.Keys, statusName})
.ToDictionary(x => (string)x.Key, x => (int)x.Count); .ToDictionary(x => (string) x.Key, x => (int) x.Count);
foreach (var key in keyMaps.Keys) foreach (var key in keyMaps.Keys)
{
if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0); if (!valuesMap.ContainsKey(key)) valuesMap.Add(key, 0);
}
var result = new Dictionary<DateTime, int>(); var result = new Dictionary<DateTime, int>();
for (var i = 0; i < keyMaps.Count; i++) for (var i = 0; i < keyMaps.Count; i++)
......
...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.SqlServer ...@@ -11,9 +11,9 @@ namespace DotNetCore.CAP.SqlServer
{ {
public class SqlServerStorage : IStorage public class SqlServerStorage : IStorage
{ {
private readonly SqlServerOptions _options;
private readonly ILogger _logger;
private readonly IDbConnection _existingConnection = null; private readonly IDbConnection _existingConnection = null;
private readonly ILogger _logger;
private readonly SqlServerOptions _options;
public SqlServerStorage(ILogger<SqlServerStorage> logger, SqlServerOptions options) public SqlServerStorage(ILogger<SqlServerStorage> logger, SqlServerOptions options)
{ {
...@@ -118,9 +118,7 @@ END;"; ...@@ -118,9 +118,7 @@ END;";
var connection = _existingConnection ?? new SqlConnection(_options.ConnectionString); var connection = _existingConnection ?? new SqlConnection(_options.ConnectionString);
if (connection.State == ConnectionState.Closed) if (connection.State == ConnectionState.Closed)
{
connection.Open(); connection.Open();
}
return connection; return connection;
} }
...@@ -133,10 +131,7 @@ END;"; ...@@ -133,10 +131,7 @@ END;";
internal void ReleaseConnection(IDbConnection connection) internal void ReleaseConnection(IDbConnection connection)
{ {
if (connection != null && !IsExistingConnection(connection)) if (connection != null && !IsExistingConnection(connection))
{
connection.Dispose(); connection.Dispose();
} }
} }
}
} }
\ No newline at end of file
...@@ -101,7 +101,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; ...@@ -101,7 +101,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
} }
} }
public async Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync() public async Task<CapReceivedMessage> GetNextReceivedMessageToBeEnqueuedAsync()
{ {
var sql = var sql =
$"SELECT TOP (1) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'"; $"SELECT TOP (1) * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Scheduled}'";
...@@ -111,7 +111,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; ...@@ -111,7 +111,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
} }
} }
public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceviedMessages() public async Task<IEnumerable<CapReceivedMessage>> GetFailedReceivedMessages()
{ {
var sql = var sql =
$"SELECT * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Failed}'"; $"SELECT * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE StatusName = '{StatusName.Failed}'";
......
...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.SqlServer ...@@ -9,10 +9,10 @@ namespace DotNetCore.CAP.SqlServer
{ {
public class SqlServerStorageTransaction : IStorageTransaction public class SqlServerStorageTransaction : IStorageTransaction
{ {
private readonly string _schema; private readonly IDbConnection _dbConnection;
private readonly IDbTransaction _dbTransaction; private readonly IDbTransaction _dbTransaction;
private readonly IDbConnection _dbConnection; private readonly string _schema;
public SqlServerStorageTransaction(SqlServerStorageConnection connection) public SqlServerStorageTransaction(SqlServerStorageConnection connection)
{ {
...@@ -28,7 +28,8 @@ namespace DotNetCore.CAP.SqlServer ...@@ -28,7 +28,8 @@ namespace DotNetCore.CAP.SqlServer
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"UPDATE [{_schema}].[Published] SET [Retries] = @Retries,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; var sql =
$"UPDATE [{_schema}].[Published] SET [Retries] = @Retries,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -36,7 +37,8 @@ namespace DotNetCore.CAP.SqlServer ...@@ -36,7 +37,8 @@ namespace DotNetCore.CAP.SqlServer
{ {
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"UPDATE [{_schema}].[Received] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;"; var sql =
$"UPDATE [{_schema}].[Received] SET [Retries] = @Retries,[Content] = @Content,[ExpiresAt] = @ExpiresAt,[StatusName]=@StatusName WHERE Id=@Id;";
_dbConnection.Execute(sql, message, _dbTransaction); _dbConnection.Execute(sql, message, _dbTransaction);
} }
...@@ -45,7 +47,8 @@ namespace DotNetCore.CAP.SqlServer ...@@ -45,7 +47,8 @@ namespace DotNetCore.CAP.SqlServer
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Publish }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish},
_dbTransaction);
} }
public void EnqueueMessage(CapReceivedMessage message) public void EnqueueMessage(CapReceivedMessage message)
...@@ -53,7 +56,8 @@ namespace DotNetCore.CAP.SqlServer ...@@ -53,7 +56,8 @@ namespace DotNetCore.CAP.SqlServer
if (message == null) throw new ArgumentNullException(nameof(message)); if (message == null) throw new ArgumentNullException(nameof(message));
var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);"; var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue { MessageId = message.Id, MessageType = MessageType.Subscribe }, _dbTransaction); _dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe},
_dbTransaction);
} }
public Task CommitAsync() public Task CommitAsync()
......
...@@ -10,7 +10,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -10,7 +10,7 @@ namespace DotNetCore.CAP.Abstractions
public abstract class CapPublisherBase : ICapPublisher, IDisposable public abstract class CapPublisherBase : ICapPublisher, IDisposable
{ {
protected IDbConnection DbConnection { get; set; } protected IDbConnection DbConnection { get; set; }
protected IDbTransaction DbTranasaction { get; set; } protected IDbTransaction DbTransaction { get; set; }
protected bool IsCapOpenedTrans { get; set; } protected bool IsCapOpenedTrans { get; set; }
protected bool IsCapOpenedConn { get; set; } protected bool IsCapOpenedConn { get; set; }
protected bool IsUsingEF { get; set; } protected bool IsUsingEF { get; set; }
...@@ -60,13 +60,15 @@ namespace DotNetCore.CAP.Abstractions ...@@ -60,13 +60,15 @@ namespace DotNetCore.CAP.Abstractions
protected abstract void PrepareConnectionForEF(); protected abstract void PrepareConnectionForEF();
protected abstract void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); protected abstract void Execute(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message);
protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, CapPublishedMessage message); protected abstract Task ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction,
CapPublishedMessage message);
protected virtual string Serialize<T>(T obj, string callbackName = null) protected virtual string Serialize<T>(T obj, string callbackName = null)
{ {
var serializer = (IContentSerializer)ServiceProvider.GetService(typeof(IContentSerializer)); var serializer = (IContentSerializer) ServiceProvider.GetService(typeof(IContentSerializer));
var message = new CapMessageDto(obj) var message = new CapMessageDto(obj)
{ {
...@@ -85,11 +87,11 @@ namespace DotNetCore.CAP.Abstractions ...@@ -85,11 +87,11 @@ namespace DotNetCore.CAP.Abstractions
IsCapOpenedConn = true; IsCapOpenedConn = true;
DbConnection.Open(); DbConnection.Open();
} }
DbTranasaction = dbTransaction; DbTransaction = dbTransaction;
if (DbTranasaction == null) if (DbTransaction == null)
{ {
IsCapOpenedTrans = true; IsCapOpenedTrans = true;
DbTranasaction = dbConnection.BeginTransaction(IsolationLevel.ReadCommitted); DbTransaction = dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
} }
} }
...@@ -97,7 +99,8 @@ namespace DotNetCore.CAP.Abstractions ...@@ -97,7 +99,8 @@ namespace DotNetCore.CAP.Abstractions
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
if (!IsUsingEF) if (!IsUsingEF)
throw new InvalidOperationException("If you are using the EntityFramework, you need to configure the DbContextType first." + throw new InvalidOperationException(
"If you are using the EntityFramework, you need to configure the DbContextType first." +
" otherwise you need to use overloaded method with IDbConnection and IDbTransaction."); " otherwise you need to use overloaded method with IDbConnection and IDbTransaction.");
} }
...@@ -105,7 +108,8 @@ namespace DotNetCore.CAP.Abstractions ...@@ -105,7 +108,8 @@ namespace DotNetCore.CAP.Abstractions
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
if (IsUsingEF) if (IsUsingEF)
throw new InvalidOperationException("If you are using the EntityFramework, you do not need to use this overloaded."); throw new InvalidOperationException(
"If you are using the EntityFramework, you do not need to use this overloaded.");
} }
private async Task PublishWithTransAsync(string name, string content) private async Task PublishWithTransAsync(string name, string content)
...@@ -117,7 +121,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -117,7 +121,7 @@ namespace DotNetCore.CAP.Abstractions
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
await ExecuteAsync(DbConnection, DbTranasaction, message); await ExecuteAsync(DbConnection, DbTransaction, message);
ClosedCap(); ClosedCap();
...@@ -133,7 +137,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -133,7 +137,7 @@ namespace DotNetCore.CAP.Abstractions
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
Execute(DbConnection, DbTranasaction, message); Execute(DbConnection, DbTransaction, message);
ClosedCap(); ClosedCap();
...@@ -144,18 +148,16 @@ namespace DotNetCore.CAP.Abstractions ...@@ -144,18 +148,16 @@ namespace DotNetCore.CAP.Abstractions
{ {
if (IsCapOpenedTrans) if (IsCapOpenedTrans)
{ {
DbTranasaction.Commit(); DbTransaction.Commit();
DbTranasaction.Dispose(); DbTransaction.Dispose();
} }
if (IsCapOpenedConn) if (IsCapOpenedConn)
{
DbConnection.Dispose(); DbConnection.Dispose();
} }
}
public void Dispose() public void Dispose()
{ {
DbTranasaction?.Dispose(); DbTransaction?.Dispose();
DbConnection?.Dispose(); DbConnection?.Dispose();
} }
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
namespace DotNetCore.CAP.Abstractions namespace DotNetCore.CAP.Abstractions
{ {
/// <summary> /// <summary>
/// A context for consumers, it used to be provider wapper of method description and received message. /// A context for consumers, it used to be provider wrapper of method description and received message.
/// </summary> /// </summary>
public class ConsumerContext public class ConsumerContext
{ {
/// <summary> /// <summary>
/// create a new instance of <see cref="ConsumerContext"/> . /// create a new instance of <see cref="ConsumerContext" /> .
/// </summary> /// </summary>
/// <param name="descriptor">consumer method descriptor. </param> /// <param name="descriptor">consumer method descriptor. </param>
/// <param name="message"> reveied message.</param> /// <param name="message"> received message.</param>
public ConsumerContext(ConsumerExecutorDescriptor descriptor, MessageContext message) public ConsumerContext(ConsumerExecutorDescriptor descriptor, MessageContext message)
{ {
ConsumerDescriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor)); ConsumerDescriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
...@@ -24,7 +24,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -24,7 +24,7 @@ namespace DotNetCore.CAP.Abstractions
public ConsumerExecutorDescriptor ConsumerDescriptor { get; set; } public ConsumerExecutorDescriptor ConsumerDescriptor { get; set; }
/// <summary> /// <summary>
/// consumer reveived message. /// consumer received message.
/// </summary> /// </summary>
public MessageContext DeliverMessage { get; set; } public MessageContext DeliverMessage { get; set; }
} }
......
...@@ -3,22 +3,22 @@ ...@@ -3,22 +3,22 @@
namespace DotNetCore.CAP.Abstractions namespace DotNetCore.CAP.Abstractions
{ {
/// <summary> /// <summary>
/// Defines an interface for selecting an cosumer service method to invoke for the current message. /// Defines an interface for selecting an consumer service method to invoke for the current message.
/// </summary> /// </summary>
public interface IConsumerServiceSelector public interface IConsumerServiceSelector
{ {
/// <summary> /// <summary>
/// Selects a set of <see cref="ConsumerExecutorDescriptor"/> candidates for the current message associated with /// Selects a set of <see cref="ConsumerExecutorDescriptor" /> candidates for the current message associated with
/// </summary> /// </summary>
/// <returns>A set of <see cref="ConsumerExecutorDescriptor"/> candidates or <c>null</c>.</returns> /// <returns>A set of <see cref="ConsumerExecutorDescriptor" /> candidates or <c>null</c>.</returns>
IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates(); IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates();
/// <summary> /// <summary>
/// Selects the best <see cref="ConsumerExecutorDescriptor"/> candidate from <paramref name="candidates"/> for the /// Selects the best <see cref="ConsumerExecutorDescriptor" /> candidate from <paramref name="candidates" /> for the
/// current message associated. /// current message associated.
/// </summary> /// </summary>
/// <param name="key">topic or exchange router key.</param> /// <param name="key">topic or exchange router key.</param>
/// <param name="candidates">the set of <see cref="ConsumerExecutorDescriptor"/> candidates.</param> /// <param name="candidates">the set of <see cref="ConsumerExecutorDescriptor" /> candidates.</param>
/// <returns></returns> /// <returns></returns>
ConsumerExecutorDescriptor ConsumerExecutorDescriptor
SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> candidates); SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> candidates);
......
...@@ -8,22 +8,22 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding ...@@ -8,22 +8,22 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding
public struct ModelBindingResult public struct ModelBindingResult
{ {
/// <summary> /// <summary>
/// Creates a <see cref="ModelBindingResult"/> representing a failed model binding operation. /// Creates a <see cref="ModelBindingResult" /> representing a failed model binding operation.
/// </summary> /// </summary>
/// <returns>A <see cref="ModelBindingResult"/> representing a failed model binding operation.</returns> /// <returns>A <see cref="ModelBindingResult" /> representing a failed model binding operation.</returns>
public static ModelBindingResult Failed() public static ModelBindingResult Failed()
{ {
return new ModelBindingResult(model: null, isSuccess: false); return new ModelBindingResult(null, false);
} }
/// <summary> /// <summary>
/// Creates a <see cref="ModelBindingResult"/> representing a successful model binding operation. /// Creates a <see cref="ModelBindingResult" /> representing a successful model binding operation.
/// </summary> /// </summary>
/// <param name="model">The model value. May be <c>null.</c></param> /// <param name="model">The model value. May be <c>null.</c></param>
/// <returns>A <see cref="ModelBindingResult"/> representing a successful model bind.</returns> /// <returns>A <see cref="ModelBindingResult" /> representing a successful model bind.</returns>
public static ModelBindingResult Success(object model) public static ModelBindingResult Success(object model)
{ {
return new ModelBindingResult(model, isSuccess: true); return new ModelBindingResult(model, true);
} }
private ModelBindingResult(object model, bool isSuccess) private ModelBindingResult(object model, bool isSuccess)
...@@ -42,27 +42,17 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding ...@@ -42,27 +42,17 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding
public override string ToString() public override string ToString()
{ {
if (IsSuccess) if (IsSuccess)
{
return $"Success '{Model}'"; return $"Success '{Model}'";
}
else
{
return $"Failed"; return $"Failed";
} }
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var other = obj as ModelBindingResult?; var other = obj as ModelBindingResult?;
if (other == null) if (other == null)
{
return false; return false;
}
else
{
return Equals(other.Value); return Equals(other.Value);
} }
}
public override int GetHashCode() public override int GetHashCode()
{ {
...@@ -81,10 +71,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding ...@@ -81,10 +71,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding
} }
/// <summary> /// <summary>
/// Compares <see cref="ModelBindingResult"/> objects for equality. /// Compares <see cref="ModelBindingResult" /> objects for equality.
/// </summary> /// </summary>
/// <param name="x">A <see cref="ModelBindingResult"/>.</param> /// <param name="x">A <see cref="ModelBindingResult" />.</param>
/// <param name="y">A <see cref="ModelBindingResult"/>.</param> /// <param name="y">A <see cref="ModelBindingResult" />.</param>
/// <returns><c>true</c> if the objects are equal, otherwise <c>false</c>.</returns> /// <returns><c>true</c> if the objects are equal, otherwise <c>false</c>.</returns>
public static bool operator ==(ModelBindingResult x, ModelBindingResult y) public static bool operator ==(ModelBindingResult x, ModelBindingResult y)
{ {
...@@ -92,10 +82,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding ...@@ -92,10 +82,10 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding
} }
/// <summary> /// <summary>
/// Compares <see cref="ModelBindingResult"/> objects for inequality. /// Compares <see cref="ModelBindingResult" /> objects for inequality.
/// </summary> /// </summary>
/// <param name="x">A <see cref="ModelBindingResult"/>.</param> /// <param name="x">A <see cref="ModelBindingResult" />.</param>
/// <param name="y">A <see cref="ModelBindingResult"/>.</param> /// <param name="y">A <see cref="ModelBindingResult" />.</param>
/// <returns><c>true</c> if the objects are not equal, otherwise <c>false</c>.</returns> /// <returns><c>true</c> if the objects are not equal, otherwise <c>false</c>.</returns>
public static bool operator !=(ModelBindingResult x, ModelBindingResult y) public static bool operator !=(ModelBindingResult x, ModelBindingResult y)
{ {
......
...@@ -4,7 +4,7 @@ namespace DotNetCore.CAP.Abstractions ...@@ -4,7 +4,7 @@ namespace DotNetCore.CAP.Abstractions
{ {
/// <inheritdoc /> /// <inheritdoc />
/// <summary> /// <summary>
/// An abstract attribute that for kafka attribute or rabbitmq attribute /// An abstract attribute that for kafka attribute or rabbit mq attribute
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
public abstract class TopicAttribute : Attribute public abstract class TopicAttribute : Attribute
...@@ -20,8 +20,8 @@ namespace DotNetCore.CAP.Abstractions ...@@ -20,8 +20,8 @@ namespace DotNetCore.CAP.Abstractions
public string Name { get; } public string Name { get; }
/// <summary> /// <summary>
/// kafak --> groups.id /// kafka --> groups.id
/// rabbitmq --> queue.name /// rabbit MQ --> queue.name
/// </summary> /// </summary>
public string Group { get; set; } = "cap.default.group"; public string Group { get; set; } = "cap.default.group";
} }
......
...@@ -7,21 +7,19 @@ using Microsoft.Extensions.DependencyInjection; ...@@ -7,21 +7,19 @@ using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
{ {
/// <summary> /// <summary>
/// app extensions for <see cref="IApplicationBuilder"/> /// app extensions for <see cref="IApplicationBuilder" />
/// </summary> /// </summary>
public static class AppBuilderExtensions public static class AppBuilderExtensions
{ {
///<summary> /// <summary>
/// Enables cap for the current application /// Enables cap for the current application
/// </summary> /// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param> /// <param name="app">The <see cref="IApplicationBuilder" /> instance this method extends.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance this method extends.</returns> /// <returns>The <see cref="IApplicationBuilder" /> instance this method extends.</returns>
public static IApplicationBuilder UseCap(this IApplicationBuilder app) public static IApplicationBuilder UseCap(this IApplicationBuilder app)
{ {
if (app == null) if (app == null)
{
throw new ArgumentNullException(nameof(app)); throw new ArgumentNullException(nameof(app));
}
CheckRequirement(app); CheckRequirement(app);
...@@ -33,9 +31,7 @@ namespace Microsoft.AspNetCore.Builder ...@@ -33,9 +31,7 @@ namespace Microsoft.AspNetCore.Builder
if (provider.GetService<DashboardOptions>() != null) if (provider.GetService<DashboardOptions>() != null)
{ {
if (provider.GetService<DiscoveryOptions>() != null) if (provider.GetService<DiscoveryOptions>() != null)
{
app.UseMiddleware<GatewayProxyMiddleware>(); app.UseMiddleware<GatewayProxyMiddleware>();
}
app.UseMiddleware<DashboardMiddleware>(); app.UseMiddleware<DashboardMiddleware>();
} }
...@@ -46,21 +42,18 @@ namespace Microsoft.AspNetCore.Builder ...@@ -46,21 +42,18 @@ namespace Microsoft.AspNetCore.Builder
{ {
var marker = app.ApplicationServices.GetService<CapMarkerService>(); var marker = app.ApplicationServices.GetService<CapMarkerService>();
if (marker == null) if (marker == null)
{ throw new InvalidOperationException(
throw new InvalidOperationException("AddCap() must be called on the service collection. eg: services.AddCap(...)"); "AddCap() must be called on the service collection. eg: services.AddCap(...)");
}
var messageQueuemarker = app.ApplicationServices.GetService<CapMessageQueueMakerService>(); var messageQueueMarker = app.ApplicationServices.GetService<CapMessageQueueMakerService>();
if (messageQueuemarker == null) if (messageQueueMarker == null)
{ throw new InvalidOperationException(
throw new InvalidOperationException("You must be config used message queue provider at AddCap() options! eg: services.AddCap(options=>{ options.UseKafka(...) })"); "You must be config used message queue provider at AddCap() options! eg: services.AddCap(options=>{ options.UseKafka(...) })");
}
var databaseMarker = app.ApplicationServices.GetService<CapDatabaseStorageMarkerService>(); var databaseMarker = app.ApplicationServices.GetService<CapDatabaseStorageMarkerService>();
if (databaseMarker == null) if (databaseMarker == null)
{ throw new InvalidOperationException(
throw new InvalidOperationException("You must be config used database provider at AddCap() options! eg: services.AddCap(options=>{ options.UseSqlServer(...) })"); "You must be config used database provider at AddCap() options! eg: services.AddCap(options=>{ options.UseSqlServer(...) })");
}
} }
} }
} }
\ No newline at end of file
...@@ -15,7 +15,6 @@ namespace DotNetCore.CAP ...@@ -15,7 +15,6 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public class CapDatabaseStorageMarkerService public class CapDatabaseStorageMarkerService
{ {
} }
/// <summary> /// <summary>
...@@ -23,7 +22,6 @@ namespace DotNetCore.CAP ...@@ -23,7 +22,6 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public class CapMessageQueueMakerService public class CapMessageQueueMakerService
{ {
} }
/// <summary> /// <summary>
...@@ -37,7 +35,7 @@ namespace DotNetCore.CAP ...@@ -37,7 +35,7 @@ namespace DotNetCore.CAP
} }
/// <summary> /// <summary>
/// Gets the <see cref="IServiceCollection"/> where MVC services are configured. /// Gets the <see cref="IServiceCollection" /> where MVC services are configured.
/// </summary> /// </summary>
public IServiceCollection Services { get; } public IServiceCollection Services { get; }
...@@ -51,7 +49,7 @@ namespace DotNetCore.CAP ...@@ -51,7 +49,7 @@ namespace DotNetCore.CAP
} }
/// <summary> /// <summary>
/// Add an <see cref="ICapPublisher"/>. /// Add an <see cref="ICapPublisher" />.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the service.</typeparam> /// <typeparam name="T">The type of the service.</typeparam>
public virtual CapBuilder AddProducerService<T>() public virtual CapBuilder AddProducerService<T>()
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using DotNetCore.CAP.Models;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
...@@ -8,8 +9,6 @@ namespace DotNetCore.CAP ...@@ -8,8 +9,6 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public class CapOptions public class CapOptions
{ {
internal IList<ICapOptionsExtension> Extensions { get; }
/// <summary> /// <summary>
/// Default value for polling delay timeout, in seconds. /// Default value for polling delay timeout, in seconds.
/// </summary> /// </summary>
...@@ -21,7 +20,7 @@ namespace DotNetCore.CAP ...@@ -21,7 +20,7 @@ namespace DotNetCore.CAP
public const int DefaultQueueProcessorCount = 2; public const int DefaultQueueProcessorCount = 2;
/// <summary> /// <summary>
/// Default succeeded message expriation timespan, in seconds. /// Default succeeded message expiration time span, in seconds.
/// </summary> /// </summary>
public const int DefaultSucceedMessageExpirationAfter = 24 * 3600; public const int DefaultSucceedMessageExpirationAfter = 24 * 3600;
...@@ -39,8 +38,10 @@ namespace DotNetCore.CAP ...@@ -39,8 +38,10 @@ namespace DotNetCore.CAP
Extensions = new List<ICapOptionsExtension>(); Extensions = new List<ICapOptionsExtension>();
} }
internal IList<ICapOptionsExtension> Extensions { get; }
/// <summary> /// <summary>
/// Productor job polling delay time. /// Producer job polling delay time.
/// Default is 15 sec. /// Default is 15 sec.
/// </summary> /// </summary>
public int PollingDelay { get; set; } public int PollingDelay { get; set; }
...@@ -53,7 +54,7 @@ namespace DotNetCore.CAP ...@@ -53,7 +54,7 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Sent or received succeed message after timespan of due, then the message will be deleted at due time. /// Sent or received succeed message after timespan of due, then the message will be deleted at due time.
/// Dafault is 24*3600 seconds. /// Default is 24*3600 seconds.
/// </summary> /// </summary>
public int SucceedMessageExpiredAfter { get; set; } public int SucceedMessageExpiredAfter { get; set; }
...@@ -66,7 +67,7 @@ namespace DotNetCore.CAP ...@@ -66,7 +67,7 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// We’ll invoke this call-back with message type,name,content when requeue failed message. /// We’ll invoke this call-back with message type,name,content when requeue failed message.
/// </summary> /// </summary>
public Action<Models.MessageType, string, string> FailedCallback { get; set; } public Action<MessageType, string, string> FailedCallback { get; set; }
/// <summary> /// <summary>
/// Registers an extension that will be executed when building services. /// Registers an extension that will be executed when building services.
......
...@@ -11,16 +11,16 @@ using Microsoft.Extensions.DependencyInjection.Extensions; ...@@ -11,16 +11,16 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
/// <summary> /// <summary>
/// Contains extension methods to <see cref="IServiceCollection"/> for configuring consistence services. /// Contains extension methods to <see cref="IServiceCollection" /> for configuring consistence services.
/// </summary> /// </summary>
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
/// <summary> /// <summary>
/// Adds and configures the consistence services for the consitence. /// Adds and configures the consistence services for the consistency.
/// </summary> /// </summary>
/// <param name="services">The services available in the application.</param> /// <param name="services">The services available in the application.</param>
/// <param name="setupAction">An action to configure the <see cref="CapOptions"/>.</param> /// <param name="setupAction">An action to configure the <see cref="CapOptions" />.</param>
/// <returns>An <see cref="CapBuilder"/> for application services.</returns> /// <returns>An <see cref="CapBuilder" /> for application services.</returns>
public static CapBuilder AddCap( public static CapBuilder AddCap(
this IServiceCollection services, this IServiceCollection services,
Action<CapOptions> setupAction) Action<CapOptions> setupAction)
...@@ -51,15 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -51,15 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection
//Executors //Executors
services.AddSingleton<IQueueExecutorFactory, QueueExecutorFactory>(); services.AddSingleton<IQueueExecutorFactory, QueueExecutorFactory>();
services.AddSingleton<IQueueExecutor, SubscibeQueueExecutor>(); services.AddSingleton<IQueueExecutor, SubscribeQueueExecutor>();
//Options and extension service //Options and extension service
var options = new CapOptions(); var options = new CapOptions();
setupAction(options); setupAction(options);
foreach (var serviceExtension in options.Extensions) foreach (var serviceExtension in options.Extensions)
{
serviceExtension.AddServices(services); serviceExtension.AddServices(services);
}
services.AddSingleton(options); services.AddSingleton(options);
return new CapBuilder(services); return new CapBuilder(services);
...@@ -69,19 +67,13 @@ namespace Microsoft.Extensions.DependencyInjection ...@@ -69,19 +67,13 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
var consumerListenerServices = new List<KeyValuePair<Type, Type>>(); var consumerListenerServices = new List<KeyValuePair<Type, Type>>();
foreach (var rejectedServices in services) foreach (var rejectedServices in services)
{
if (rejectedServices.ImplementationType != null if (rejectedServices.ImplementationType != null
&& typeof(ICapSubscribe).IsAssignableFrom(rejectedServices.ImplementationType)) && typeof(ICapSubscribe).IsAssignableFrom(rejectedServices.ImplementationType))
{
consumerListenerServices.Add(new KeyValuePair<Type, Type>(typeof(ICapSubscribe), consumerListenerServices.Add(new KeyValuePair<Type, Type>(typeof(ICapSubscribe),
rejectedServices.ImplementationType)); rejectedServices.ImplementationType));
}
}
foreach (var service in consumerListenerServices) foreach (var service in consumerListenerServices)
{
services.AddTransient(service.Key, service.Value); services.AddTransient(service.Key, service.Value);
} }
} }
}
} }
\ No newline at end of file
...@@ -6,26 +6,26 @@ using System.Threading; ...@@ -6,26 +6,26 @@ using System.Threading;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
#region Cache<T> class #region Cache<T> class
/// <summary> /// <summary>
/// This is a generic cache subsystem based on key/value pairs, where key is generic, too. Key must be unique. /// This is a generic cache subsystem based on key/value pairs, where key is generic, too. Key must be unique.
/// Every cache entry has its own timeout. /// Every cache entry has its own timeout.
/// Cache is thread safe and will delete expired entries on its own using System.Threading.Timers (which run on <see cref="ThreadPool"/> threads). /// Cache is thread safe and will delete expired entries on its own using System.Threading.Timers (which run on
/// <see cref="ThreadPool" /> threads).
/// </summary> /// </summary>
public class Cache<K, T> : IDisposable public class Cache<K, T> : IDisposable
{ {
#region Constructor and class members #region Constructor and class members
/// <summary>
/// Initializes a new instance of the <see cref="Cache{K,T}"/> class.
/// </summary>
public Cache() { }
private Dictionary<K, T> _cache = new Dictionary<K, T>(); private readonly Dictionary<K, T> _cache = new Dictionary<K, T>();
private Dictionary<K, Timer> _timers = new Dictionary<K, Timer>(); private readonly Dictionary<K, Timer> _timers = new Dictionary<K, Timer>();
private ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
#endregion #endregion
#region IDisposable implementation & Clear #region IDisposable implementation & Clear
private bool disposed = false;
private bool disposed;
/// <summary> /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
...@@ -40,7 +40,8 @@ namespace DotNetCore.CAP ...@@ -40,7 +40,8 @@ namespace DotNetCore.CAP
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <param name="disposing"> /// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> /// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!disposed) if (!disposed)
...@@ -67,20 +68,26 @@ namespace DotNetCore.CAP ...@@ -67,20 +68,26 @@ namespace DotNetCore.CAP
{ {
try try
{ {
foreach (Timer t in _timers.Values) foreach (var t in _timers.Values)
t.Dispose(); t.Dispose();
} }
catch catch
{ } {
}
_timers.Clear(); _timers.Clear();
_cache.Clear(); _cache.Clear();
} }
finally { _locker.ExitWriteLock(); } finally
{
_locker.ExitWriteLock();
}
} }
#endregion #endregion
#region CheckTimer #region CheckTimer
// Checks whether a specific timer already exists and adds a new one, if not // Checks whether a specific timer already exists and adds a new one, if not
private void CheckTimer(K key, TimeSpan? cacheTimeout, bool restartTimerIfExists) private void CheckTimer(K key, TimeSpan? cacheTimeout, bool restartTimerIfExists)
{ {
...@@ -89,38 +96,45 @@ namespace DotNetCore.CAP ...@@ -89,38 +96,45 @@ namespace DotNetCore.CAP
if (_timers.TryGetValue(key, out timer)) if (_timers.TryGetValue(key, out timer))
{ {
if (restartTimerIfExists) if (restartTimerIfExists)
{
timer.Change( timer.Change(
cacheTimeout ?? Timeout.InfiniteTimeSpan, cacheTimeout ?? Timeout.InfiniteTimeSpan,
Timeout.InfiniteTimeSpan); Timeout.InfiniteTimeSpan);
} }
}
else else
{
_timers.Add( _timers.Add(
key, key,
new Timer( new Timer(
new TimerCallback(RemoveByTimer), RemoveByTimer,
key, key,
cacheTimeout ?? Timeout.InfiniteTimeSpan, cacheTimeout ?? Timeout.InfiniteTimeSpan,
Timeout.InfiniteTimeSpan)); Timeout.InfiniteTimeSpan));
} }
}
private void RemoveByTimer(object state) private void RemoveByTimer(object state)
{ {
Remove((K)state); Remove((K) state);
} }
#endregion #endregion
#region AddOrUpdate, Get, Remove, Exists, Clear #region AddOrUpdate, Get, Remove, Exists, Clear
/// <summary> /// <summary>
/// Adds or updates the specified cache-key with the specified cacheObject and applies a specified timeout (in seconds) to this key. /// Adds or updates the specified cache-key with the specified cacheObject and applies a specified timeout (in seconds)
/// to this key.
/// </summary> /// </summary>
/// <param name="key">The cache-key to add or update.</param> /// <param name="key">The cache-key to add or update.</param>
/// <param name="cacheObject">The cache object to store.</param> /// <param name="cacheObject">The cache object to store.</param>
/// <param name="cacheTimeout">The cache timeout (lifespan) of this object. Must be 1 or greater. /// <param name="cacheTimeout">
/// Specify Timeout.Infinite to keep the entry forever.</param> /// The cache timeout (lifespan) of this object. Must be 1 or greater.
/// <param name="restartTimerIfExists">(Optional). If set to <c>true</c>, the timer for this cacheObject will be reset if the object already /// Specify Timeout.Infinite to keep the entry forever.
/// exists in the cache. (Default = false).</param> /// </param>
/// <param name="restartTimerIfExists">
/// (Optional). If set to <c>true</c>, the timer for this cacheObject will be reset if the object already
/// exists in the cache. (Default = false).
/// </param>
public void AddOrUpdate(K key, T cacheObject, TimeSpan? cacheTimeout, bool restartTimerIfExists = false) public void AddOrUpdate(K key, T cacheObject, TimeSpan? cacheTimeout, bool restartTimerIfExists = false)
{ {
if (disposed) return; if (disposed) return;
...@@ -135,11 +149,15 @@ namespace DotNetCore.CAP ...@@ -135,11 +149,15 @@ namespace DotNetCore.CAP
else else
_cache[key] = cacheObject; _cache[key] = cacheObject;
} }
finally { _locker.ExitWriteLock(); } finally
{
_locker.ExitWriteLock();
}
} }
/// <summary> /// <summary>
/// Adds or updates the specified cache-key with the specified cacheObject and applies <c>Timeout.Infinite</c> to this key. /// Adds or updates the specified cache-key with the specified cacheObject and applies <c>Timeout.Infinite</c> to this
/// key.
/// </summary> /// </summary>
/// <param name="key">The cache-key to add or update.</param> /// <param name="key">The cache-key to add or update.</param>
/// <param name="cacheObject">The cache object to store.</param> /// <param name="cacheObject">The cache object to store.</param>
...@@ -168,9 +186,12 @@ namespace DotNetCore.CAP ...@@ -168,9 +186,12 @@ namespace DotNetCore.CAP
try try
{ {
T rv; T rv;
return (_cache.TryGetValue(key, out rv) ? rv : default(T)); return _cache.TryGetValue(key, out rv) ? rv : default(T);
}
finally
{
_locker.ExitReadLock();
} }
finally { _locker.ExitReadLock(); }
} }
/// <summary> /// <summary>
...@@ -192,7 +213,10 @@ namespace DotNetCore.CAP ...@@ -192,7 +213,10 @@ namespace DotNetCore.CAP
{ {
return _cache.TryGetValue(key, out value); return _cache.TryGetValue(key, out value);
} }
finally { _locker.ExitReadLock(); } finally
{
_locker.ExitReadLock();
}
} }
/// <summary> /// <summary>
...@@ -210,15 +234,23 @@ namespace DotNetCore.CAP ...@@ -210,15 +234,23 @@ namespace DotNetCore.CAP
where keyPattern(k) where keyPattern(k)
select k).ToList(); select k).ToList();
foreach (K workKey in removers) foreach (var workKey in removers)
{
try
{ {
try { _timers[workKey].Dispose(); } _timers[workKey].Dispose();
catch { } }
catch
{
}
_timers.Remove(workKey); _timers.Remove(workKey);
_cache.Remove(workKey); _cache.Remove(workKey);
} }
} }
finally { _locker.ExitWriteLock(); } finally
{
_locker.ExitWriteLock();
}
} }
/// <summary> /// <summary>
...@@ -235,13 +267,21 @@ namespace DotNetCore.CAP ...@@ -235,13 +267,21 @@ namespace DotNetCore.CAP
{ {
if (_cache.ContainsKey(key)) if (_cache.ContainsKey(key))
{ {
try { _timers[key].Dispose(); } try
catch { } {
_timers[key].Dispose();
}
catch
{
}
_timers.Remove(key); _timers.Remove(key);
_cache.Remove(key); _cache.Remove(key);
} }
} }
finally { _locker.ExitWriteLock(); } finally
{
_locker.ExitWriteLock();
}
} }
/// <summary> /// <summary>
...@@ -258,33 +298,40 @@ namespace DotNetCore.CAP ...@@ -258,33 +298,40 @@ namespace DotNetCore.CAP
{ {
return _cache.ContainsKey(key); return _cache.ContainsKey(key);
} }
finally { _locker.ExitReadLock(); } finally
{
_locker.ExitReadLock();
}
} }
#endregion #endregion
} }
#endregion #endregion
#region Other Cache classes (derived) #region Other Cache classes (derived)
/// <summary> /// <summary>
/// This is a generic cache subsystem based on key/value pairs, where key is a string. /// This is a generic cache subsystem based on key/value pairs, where key is a string.
/// You can add any item to this cache as long as the key is unique, so treat keys as something like namespaces and build them with a /// You can add any item to this cache as long as the key is unique, so treat keys as something like namespaces and
/// build them with a
/// specific system/syntax in your application. /// specific system/syntax in your application.
/// Every cache entry has its own timeout. /// Every cache entry has its own timeout.
/// Cache is thread safe and will delete expired entries on its own using System.Threading.Timers (which run on <see cref="ThreadPool"/> threads). /// Cache is thread safe and will delete expired entries on its own using System.Threading.Timers (which run on
/// <see cref="ThreadPool" /> threads).
/// </summary> /// </summary>
//public class Cache<T> : Cache<string, T>
//{
//}
/// <summary> /// <summary>
/// The non-generic Cache class instanciates a Cache{object} that can be used with any type of (mixed) contents. /// The non-generic Cache class instanciates a Cache{object} that can be used with any type of (mixed) contents.
/// It also publishes a static <c>.Global</c> member, so a cache can be used even without creating a dedicated instance. /// It also publishes a static <c>.Global</c> member, so a cache can be used even without creating a dedicated
/// instance.
/// The <c>.Global</c> member is lazy instanciated. /// The <c>.Global</c> member is lazy instanciated.
/// </summary> /// </summary>
public class CapCache : Cache<string, object> public class CapCache : Cache<string, object>
{ {
#region Static Global Cache instance #region Static Global Cache instance
private static Lazy<CapCache> global = new Lazy<CapCache>();
private static readonly Lazy<CapCache> global = new Lazy<CapCache>();
/// <summary> /// <summary>
/// Gets the global shared cache instance valid for the entire process. /// Gets the global shared cache instance valid for the entire process.
/// </summary> /// </summary>
...@@ -292,8 +339,9 @@ namespace DotNetCore.CAP ...@@ -292,8 +339,9 @@ namespace DotNetCore.CAP
/// The global shared cache instance. /// The global shared cache instance.
/// </value> /// </value>
public static CapCache Global => global.Value; public static CapCache Global => global.Value;
#endregion #endregion
} }
#endregion
#endregion
} }
\ No newline at end of file
...@@ -28,7 +28,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -28,7 +28,7 @@ namespace DotNetCore.CAP.Dashboard
_command(context, id); _command(context, id);
} }
context.Response.StatusCode = (int)HttpStatusCode.NoContent; context.Response.StatusCode = (int) HttpStatusCode.NoContent;
} }
} }
} }
\ No newline at end of file
...@@ -10,12 +10,13 @@ namespace DotNetCore.CAP ...@@ -10,12 +10,13 @@ namespace DotNetCore.CAP
{ {
public class DashboardMiddleware public class DashboardMiddleware
{ {
private readonly DashboardOptions _options;
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IStorage _storage; private readonly DashboardOptions _options;
private readonly RouteCollection _routes; private readonly RouteCollection _routes;
private readonly IStorage _storage;
public DashboardMiddleware(RequestDelegate next, DashboardOptions options, IStorage storage, RouteCollection routes) public DashboardMiddleware(RequestDelegate next, DashboardOptions options, IStorage storage,
RouteCollection routes)
{ {
_next = next ?? throw new ArgumentNullException(nameof(next)); _next = next ?? throw new ArgumentNullException(nameof(next));
_options = options ?? throw new ArgumentNullException(nameof(options)); _options = options ?? throw new ArgumentNullException(nameof(options));
...@@ -40,17 +41,15 @@ namespace DotNetCore.CAP ...@@ -40,17 +41,15 @@ namespace DotNetCore.CAP
var findResult = _routes.FindDispatcher(context.Request.Path.Value); var findResult = _routes.FindDispatcher(context.Request.Path.Value);
if (findResult == null) if (findResult == null)
{
return _next.Invoke(context); return _next.Invoke(context);
}
if (_options.Authorization.Any(filter => !filter.Authorize(dashboardContext))) if (_options.Authorization.Any(filter => !filter.Authorize(dashboardContext)))
{ {
var isAuthenticated = context.User?.Identity?.IsAuthenticated; var isAuthenticated = context.User?.Identity?.IsAuthenticated;
context.Response.StatusCode = isAuthenticated == true context.Response.StatusCode = isAuthenticated == true
? (int)HttpStatusCode.Forbidden ? (int) HttpStatusCode.Forbidden
: (int)HttpStatusCode.Unauthorized; : (int) HttpStatusCode.Unauthorized;
return Task.CompletedTask; return Task.CompletedTask;
} }
......
...@@ -10,7 +10,7 @@ namespace DotNetCore.CAP ...@@ -10,7 +10,7 @@ namespace DotNetCore.CAP
{ {
AppPath = "/"; AppPath = "/";
PathMatch = "/cap"; PathMatch = "/cap";
Authorization = new[] { new LocalRequestsOnlyAuthorizationFilter() }; Authorization = new[] {new LocalRequestsOnlyAuthorizationFilter()};
StatsPollingInterval = 2000; StatsPollingInterval = 2000;
} }
...@@ -28,5 +28,4 @@ namespace DotNetCore.CAP ...@@ -28,5 +28,4 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
public int StatsPollingInterval { get; set; } public int StatsPollingInterval { get; set; }
} }
} }
\ No newline at end of file
using System; using System;
using DotNetCore.CAP;
using DotNetCore.CAP.Dashboard;
using DotNetCore.CAP.Dashboard.GatewayProxy;
using DotNetCore.CAP.Dashboard.GatewayProxy.Requester;
using Microsoft.Extensions.DependencyInjection;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
using Dashboard;
using Dashboard.GatewayProxy;
using Dashboard.GatewayProxy.Requester;
using Microsoft.Extensions.DependencyInjection;
internal sealed class DashboardOptionsExtension : ICapOptionsExtension internal sealed class DashboardOptionsExtension : ICapOptionsExtension
{ {
private readonly Action<DashboardOptions> _options; private readonly Action<DashboardOptions> _options;
...@@ -31,13 +31,11 @@ namespace DotNetCore.CAP ...@@ -31,13 +31,11 @@ namespace DotNetCore.CAP
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
using DotNetCore.CAP;
public static class CapOptionsExtensions public static class CapOptionsExtensions
{ {
public static CapOptions UseDashboard(this CapOptions capOptions) public static CapOptions UseDashboard(this CapOptions capOptions)
{ {
return capOptions.UseDashboard(opt => {}); return capOptions.UseDashboard(opt => { });
} }
public static CapOptions UseDashboard(this CapOptions capOptions, Action<DashboardOptions> options) public static CapOptions UseDashboard(this CapOptions capOptions, Action<DashboardOptions> options)
......
...@@ -22,12 +22,10 @@ namespace DotNetCore.CAP.Dashboard ...@@ -22,12 +22,10 @@ namespace DotNetCore.CAP.Dashboard
protected override void WriteResponse(DashboardResponse response) protected override void WriteResponse(DashboardResponse response)
{ {
foreach (var resourceName in _resourceNames) foreach (var resourceName in _resourceNames)
{
WriteResource( WriteResource(
response, response,
_assembly, _assembly,
$"{_baseNamespace}.{resourceName}"); $"{_baseNamespace}.{resourceName}");
} }
} }
}
} }
\ No newline at end of file
...@@ -20,18 +20,14 @@ namespace DotNetCore.CAP.Dashboard ...@@ -20,18 +20,14 @@ namespace DotNetCore.CAP.Dashboard
if (!"POST".Equals(request.Method, StringComparison.OrdinalIgnoreCase)) if (!"POST".Equals(request.Method, StringComparison.OrdinalIgnoreCase))
{ {
response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; response.StatusCode = (int) HttpStatusCode.MethodNotAllowed;
return Task.FromResult(false); return Task.FromResult(false);
} }
if (_command(context)) if (_command(context))
{ response.StatusCode = (int) HttpStatusCode.NoContent;
response.StatusCode = (int)HttpStatusCode.NoContent;
}
else else
{
response.StatusCode = 422; response.StatusCode = 422;
}
return Task.FromResult(true); return Task.FromResult(true);
} }
......
...@@ -12,34 +12,32 @@ body { ...@@ -12,34 +12,32 @@ body {
} }
/* Wrapper for page content to push down footer */ /* Wrapper for page content to push down footer */
#wrap { #wrap {
min-height: 100%;
height: auto !important; height: auto !important;
height: 100%; height: 100%;
/* Negative indent footer by its height */ /* Negative indent footer by its height */
margin: 0 auto -60px; margin: 0 auto -60px;
min-height: 100%;
/* Pad bottom by footer height */ /* Pad bottom by footer height */
padding: 0 0 60px; padding: 0 0 60px;
} }
/* Set the fixed height of the footer here */ /* Set the fixed height of the footer here */
#footer {
background-color: #f5f5f5; #footer { background-color: #f5f5f5; }
}
/* Custom page CSS /* Custom page CSS
-------------------------------------------------- */ -------------------------------------------------- */
.container .credit { .container .credit { margin: 20px 0; }
margin: 20px 0;
}
.page-header { .page-header {
margin-top: 0; margin-top: 0;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
} }
.btn-death { .btn-death {
...@@ -48,37 +46,33 @@ body { ...@@ -48,37 +46,33 @@ body {
color: #fff; color: #fff;
} }
.btn-death:hover { .btn-death:hover {
background-color: #666; background-color: #666;
border-color: #555; border-color: #555;
color: #fff; color: #fff;
}
.list-group .list-group-item .glyphicon {
margin-right: 3px;
} }
.list-group .list-group-item .glyphicon { margin-right: 3px; }
.breadcrumb { .breadcrumb {
margin-bottom: 10px;
background-color: inherit; background-color: inherit;
margin-bottom: 10px;
padding: 0; padding: 0;
} }
.btn-toolbar-label { .btn-toolbar-label {
padding: 7px 0;
vertical-align: middle;
display: inline-block; display: inline-block;
margin-left: 5px; margin-left: 5px;
padding: 7px 0;
vertical-align: middle;
} }
.btn-toolbar-label-sm { .btn-toolbar-label-sm { padding: 5px 0; }
padding: 5px 0;
}
.btn-toolbar-spacer { .btn-toolbar-spacer {
width: 5px;
display: inline-block; display: inline-block;
height: 1px; height: 1px;
width: 5px;
} }
a:hover .label-hover { a:hover .label-hover {
...@@ -86,31 +80,23 @@ a:hover .label-hover { ...@@ -86,31 +80,23 @@ a:hover .label-hover {
color: #fff !important; color: #fff !important;
} }
.expander { .expander { cursor: pointer; }
cursor: pointer;
}
.expandable { .expandable { display: none; }
display: none;
}
.table-inner { .table-inner {
margin-bottom: 7px;
font-size: 90%; font-size: 90%;
margin-bottom: 7px;
} }
.min-width { .min-width {
width: 1%;
white-space: nowrap; white-space: nowrap;
width: 1%;
} }
.align-right { .align-right { text-align: right; }
text-align: right;
}
.table > tbody > tr.hover:hover > td, .table > tbody > tr.hover:hover > th { .table > tbody > tr.hover:hover > td, .table > tbody > tr.hover:hover > th { background-color: #f9f9f9; }
background-color: #f9f9f9;
}
.table > tbody > tr.highlight > td, .table > tbody > tr.highlight > th { .table > tbody > tr.highlight > td, .table > tbody > tr.highlight > th {
background-color: #fcf8e3; background-color: #fcf8e3;
...@@ -122,246 +108,196 @@ a:hover .label-hover { ...@@ -122,246 +108,196 @@ a:hover .label-hover {
border-color: #f5e8ce; border-color: #f5e8ce;
} }
.word-break { .word-break { word-break: break-all; }
word-break: break-all;
}
/* Statistics widget /* Statistics widget
-------------------------------------------------- */ -------------------------------------------------- */
#stats .list-group-item { #stats .list-group-item {
border-color: #e7e7e7;
background-color: #f8f8f8; background-color: #f8f8f8;
border-color: #e7e7e7;
} }
#stats a.list-group-item { #stats a.list-group-item { color: #777; }
color: #777;
}
#stats a.list-group-item:hover, #stats a.list-group-item:hover,
#stats a.list-group-item:focus { #stats a.list-group-item:focus { color: #333; }
color: #333;
}
#stats .list-group-item.active, #stats .list-group-item.active,
#stats .list-group-item.active:hover, #stats .list-group-item.active:hover,
#stats .list-group-item.active:focus { #stats .list-group-item.active:focus {
color: #555;
background-color: #e7e7e7; background-color: #e7e7e7;
border-color: #e7e7e7; border-color: #e7e7e7;
color: #555;
} }
.table td.failed-job-details { .table td.failed-job-details {
padding-top: 0;
padding-bottom: 0;
border-top: none;
background-color: #f5f5f5; background-color: #f5f5f5;
border-top: none;
padding-bottom: 0;
padding-top: 0;
} }
.obsolete-data, .obsolete-data a, .obsolete-data pre, .obsolete-data .label { .obsolete-data, .obsolete-data a, .obsolete-data pre, .obsolete-data .label { color: #999; }
color: #999;
}
.obsolete-data pre, .obsolete-data .label { .obsolete-data pre, .obsolete-data .label { background-color: #f5f5f5; }
background-color: #f5f5f5;
}
.obsolete-data .glyphicon-question-sign { .obsolete-data .glyphicon-question-sign {
font-size: 80%;
color: #999; color: #999;
} font-size: 80%;
}
.stack-trace { .stack-trace {
padding: 10px;
border: none; border: none;
padding: 10px;
} }
.st-type { .st-type { font-weight: bold; }
font-weight: bold;
}
.st-param-name { .st-param-name { color: #666; }
color: #666;
}
.st-file { .st-file { color: #999; }
color: #999;
}
.st-method { .st-method {
color: #00008B; color: #00008B;
font-weight: bold; font-weight: bold;
} }
.st-line { .st-line { color: #8B008B; }
color: #8B008B;
}
.width-200 { .width-200 { width: 200px; }
width: 200px;
}
.btn-toolbar-top { .btn-toolbar-top { margin-bottom: 10px; }
margin-bottom: 10px;
}
.paginator .btn { .paginator .btn { color: #428bca; }
color: #428bca;
}
.paginator .btn.active { .paginator .btn.active { color: #333; }
color: #333;
}
/* Job Snippet styles */ /* Job Snippet styles */
.job-snippet { .job-snippet {
-ms-border-radius: 4px;
background-color: #f5f5f5;
border-radius: 4px;
display: table;
margin-bottom: 20px; margin-bottom: 20px;
padding: 15px; padding: 15px;
display: table;
width: 100%; width: 100%;
-ms-border-radius: 4px;
border-radius: 4px;
background-color: #f5f5f5;
} }
.job-snippet > * { .job-snippet > * {
display: table-cell; display: table-cell;
vertical-align: top; vertical-align: top;
}
.job-snippet-code {
vertical-align: top;
} }
.job-snippet-code pre { .job-snippet-code { vertical-align: top; }
border: none;
margin: 0; .job-snippet-code pre {
background: inherit;
padding: 0;
-ms-border-radius: 0; -ms-border-radius: 0;
background: inherit;
border: none;
border-radius: 0; border-radius: 0;
font-size: 14px; font-size: 14px;
} margin: 0;
padding: 0;
}
.job-snippet-code code { .job-snippet-code code {
display: block;
color: black;
background-color: #f5f5f5; background-color: #f5f5f5;
} color: black;
display: block;
}
.job-snippet-code pre .comment { .job-snippet-code pre .comment { color: rgb(0, 128, 0); }
color: rgb(0, 128, 0);
}
.job-snippet-code pre .keyword { .job-snippet-code pre .keyword { color: rgb(0, 0, 255); }
color: rgb(0, 0, 255);
}
.job-snippet-code pre .string { .job-snippet-code pre .string { color: rgb(163, 21, 21); }
color: rgb(163, 21, 21);
}
.job-snippet-code pre .type { .job-snippet-code pre .type { color: rgb(43, 145, 175); }
color: rgb(43, 145, 175);
}
.job-snippet-code pre .xmldoc { .job-snippet-code pre .xmldoc { color: rgb(128, 128, 128); }
color: rgb(128, 128, 128);
}
.job-snippet-properties { .job-snippet-properties {
max-width: 200px; max-width: 200px;
padding-left: 5px; padding-left: 5px;
} }
.job-snippet-properties dl { .job-snippet-properties dl { margin: 0; }
margin: 0;
}
.job-snippet-properties dl dt { .job-snippet-properties dl dt {
color: #999; color: #999;
text-shadow: 0 1px white;
font-weight: normal; font-weight: normal;
} text-shadow: 0 1px white;
}
.job-snippet-properties dl dd { .job-snippet-properties dl dd {
margin-left: 0;
margin-bottom: 5px; margin-bottom: 5px;
} margin-left: 0;
}
.job-snippet-properties pre { .job-snippet-properties pre {
background-color: white;
-webkit-box-shadow: none;
-ms-box-shadow: none; -ms-box-shadow: none;
padding: 2px 4px; -webkit-box-shadow: none;
background-color: white;
border: none; border: none;
margin: 0; margin: 0;
} padding: 2px 4px;
}
.job-snippet-properties code { .job-snippet-properties code { color: black; }
color: black;
}
.state-card { .state-card {
position: relative; -ms-border-radius: 3px;
display: block;
margin-bottom: 7px;
padding: 12px;
background-color: #fff; background-color: #fff;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
-ms-border-radius: 3px;
border-radius: 3px; border-radius: 3px;
display: block;
margin-bottom: 7px;
padding: 12px;
position: relative;
} }
.state-card-title { .state-card-title { margin-bottom: 0; }
margin-bottom: 0;
}
.state-card-title .pull-right { .state-card-title .pull-right { margin-top: 3px; }
margin-top: 3px;
}
.state-card-text { .state-card-text {
margin-top: 5px;
margin-bottom: 0; margin-bottom: 0;
margin-top: 5px;
} }
.state-card h4 { .state-card h4 { margin-top: 0; }
margin-top: 0;
}
.state-card-body { .state-card-body {
padding: 10px;
margin: 10px -12px -12px -12px;
-ms-border-bottom-left-radius: 3px; -ms-border-bottom-left-radius: 3px;
border-bottom-left-radius: 3px;
-ms-border-bottom-right-radius: 3px; -ms-border-bottom-right-radius: 3px;
border-bottom-right-radius: 3px;
background-color: #f5f5f5; background-color: #f5f5f5;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
margin: 10px -12px -12px -12px;
padding: 10px;
} }
.state-card-body dl { .state-card-body dl {
margin-top: 5px;
margin-bottom: 0; margin-bottom: 0;
} margin-top: 5px;
}
.state-card-body pre { .state-card-body pre {
white-space: pre-wrap; /* CSS 3 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
background: transparent; background: transparent;
padding: 0; padding: 0;
} white-space: pre-wrap; /* CSS 3 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
.state-card-body .stack-trace { .state-card-body .stack-trace {
background-color: transparent; background-color: transparent;
padding: 0 20px;
margin-bottom: 0; margin-bottom: 0;
} padding: 0 20px;
}
.state-card-body .exception-type { .state-card-body .exception-type { margin-top: 0; }
margin-top: 0;
}
/* Job History styles */ /* Job History styles */
...@@ -370,17 +306,15 @@ a:hover .label-hover { ...@@ -370,17 +306,15 @@ a:hover .label-hover {
opacity: 0.8; opacity: 0.8;
} }
.job-history.job-history-current { .job-history.job-history-current { opacity: 1.0; }
opacity: 1.0;
}
.job-history-heading { .job-history-heading {
padding: 5px 10px;
color: #666;
-ms-border-top-left-radius: 4px; -ms-border-top-left-radius: 4px;
border-top-left-radius: 4px;
-ms-border-top-right-radius: 4px; -ms-border-top-right-radius: 4px;
border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
color: #666;
padding: 5px 10px;
} }
.job-history-body { .job-history-body {
...@@ -389,140 +323,118 @@ a:hover .label-hover { ...@@ -389,140 +323,118 @@ a:hover .label-hover {
} }
.job-history-title { .job-history-title {
margin-top: 0;
margin-bottom: 2px; margin-bottom: 2px;
margin-top: 0;
} }
.job-history dl { .job-history dl {
margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 5px;
} }
.job-history .stack-trace { .job-history .stack-trace {
background-color: transparent; background-color: transparent;
padding: 0 20px;
margin-bottom: 5px; margin-bottom: 5px;
padding: 0 20px;
} }
.job-history .exception-type { .job-history .exception-type { margin-top: 0; }
margin-top: 0;
}
.job-history-current .job-history-heading, .job-history-current .job-history-heading,
.job-history-current small { .job-history-current small { color: white; }
color: white;
}
a.job-method { a.job-method { color: inherit; }
color: inherit;
}
.list-group .glyphicon { .list-group .glyphicon { top: 2px; }
top: 2px;
}
span.metric { span.metric {
-moz-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-ms-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-o-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-webkit-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
background-color: transparent;
border: solid 1px;
border-radius: 10px;
display: inline-block; display: inline-block;
min-width: 10px;
padding: 2px 6px;
font-size: 12px; font-size: 12px;
line-height: 1; line-height: 1;
min-width: 10px;
padding: 2px 6px;
text-align: center; text-align: center;
white-space: nowrap;
vertical-align: baseline;
background-color: transparent;
border-radius: 10px;
border: solid 1px;
-webkit-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-moz-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-ms-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
-o-transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
vertical-align: baseline;
white-space: nowrap;
} }
span.metric.highlighted { span.metric.highlighted {
font-weight: bold;
color: #fff !important; color: #fff !important;
} font-weight: bold;
}
span.metric-default { span.metric-default {
color: #777;
border-color: #777; border-color: #777;
color: #777;
} }
span.metric-default.highlighted { span.metric-default.highlighted { background-color: #777; }
background-color: #777;
}
div.metric { div.metric {
border: solid 1px transparent;
-ms-border-radius: 4px; -ms-border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
border: solid 1px transparent;
border-radius: 4px; border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,.05); box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
box-shadow: 0 1px 1px rgba(0,0,0,.05);
margin-bottom: 20px; margin-bottom: 20px;
transition: color .1s ease-out, background .1s ease-out, border .1s ease-out; transition: color .1s ease-out, background .1s ease-out, border .1s ease-out;
} }
div.metric .metric-body { div.metric .metric-body {
padding: 15px 15px 0;
font-size: 26px; font-size: 26px;
padding: 15px 15px 0;
text-align: center; text-align: center;
} }
div.metric .metric-description { div.metric .metric-description {
padding: 0 15px 15px; padding: 0 15px 15px;
text-align: center; text-align: center;
} }
div.metric.metric-default { div.metric.metric-default { border-color: #ddd; }
border-color: #ddd;
}
div.metric-info, div.metric-info,
span.metric-info { span.metric-info {
color: #5bc0de;
border-color: #5bc0de; border-color: #5bc0de;
color: #5bc0de;
} }
span.metric-info.highlighted { span.metric-info.highlighted { background-color: #5bc0de; }
background-color: #5bc0de;
}
div.metric-warning, div.metric-warning,
span.metric-warning { span.metric-warning {
color: #f0ad4e;
border-color: #f0ad4e; border-color: #f0ad4e;
color: #f0ad4e;
} }
span.metric-warning.highlighted { span.metric-warning.highlighted { background-color: #f0ad4e; }
background-color: #f0ad4e;
}
div.metric-success, div.metric-success,
span.metric-success { span.metric-success {
color: #5cb85c;
border-color: #5cb85c; border-color: #5cb85c;
color: #5cb85c;
} }
span.metric-success.highlighted { span.metric-success.highlighted { background-color: #5cb85c; }
background-color: #5cb85c;
}
div.metric-danger, div.metric-danger,
span.metric-danger { span.metric-danger {
color: #d9534f;
border-color: #d9534f; border-color: #d9534f;
color: #d9534f;
} }
span.metric-danger.highlighted { span.metric-danger.highlighted { background-color: #d9534f; }
background-color: #d9534f;
}
span.metric-null, span.metric-null,
div.metric-null { div.metric-null { display: none; }
display: none;
}
@media (min-width: 992px) { @media (min-width: 992px) {
#stats { #stats {
...@@ -532,23 +444,17 @@ div.metric-null { ...@@ -532,23 +444,17 @@ div.metric-null {
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
#stats { #stats { width: 262.5px; }
width: 262.5px;
}
} }
.subscribe-table td { .subscribe-table td { vertical-align: middle !important; }
vertical-align: middle !important;
}
.subscribe-table td[rowspan] { .subscribe-table td[rowspan] { font-weight: bold; }
font-weight: bold;
}
#legend { #legend {
background: rgba(173, 169, 169, 0.13); background: rgba(173, 169, 169, 0.13);
color: #000;
position: absolute; position: absolute;
top: 110px;
right: 20px; right: 20px;
color: #000; top: 110px;
} }
\ No newline at end of file
(function (cap) { (function(cap) {
cap.config = { cap.config = {
pollInterval: $("#capConfig").data("pollinterval"), pollInterval: $("#capConfig").data("pollinterval"),
pollUrl: $("#capConfig").data("pollurl"), pollUrl: $("#capConfig").data("pollurl"),
locale: document.documentElement.lang locale: document.documentElement.lang
}; };
cap.Metrics = (function () { cap.Metrics = (function() {
function Metrics() { function Metrics() {
this._metrics = {}; this._metrics = {};
} }
Metrics.prototype.addElement = function (name, element) { Metrics.prototype.addElement = function(name, element) {
if (!(name in this._metrics)) { if (!(name in this._metrics)) {
this._metrics[name] = []; this._metrics[name] = [];
} }
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
this._metrics[name].push(element); this._metrics[name].push(element);
}; };
Metrics.prototype.getElements = function (name) { Metrics.prototype.getElements = function(name) {
if (!(name in this._metrics)) { if (!(name in this._metrics)) {
return []; return [];
} }
...@@ -26,11 +26,11 @@ ...@@ -26,11 +26,11 @@
return this._metrics[name]; return this._metrics[name];
}; };
Metrics.prototype.getNames = function () { Metrics.prototype.getNames = function() {
var result = []; const result = [];
var metrics = this._metrics; const metrics = this._metrics;
for (var name in metrics) { for (let name in metrics) {
if (metrics.hasOwnProperty(name)) { if (metrics.hasOwnProperty(name)) {
result.push(name); result.push(name);
} }
...@@ -42,14 +42,14 @@ ...@@ -42,14 +42,14 @@
return Metrics; return Metrics;
})(); })();
var BaseGraph = function () { var BaseGraph = function() {
this.height = 200; this.height = 200;
}; };
BaseGraph.prototype.update = function () { BaseGraph.prototype.update = function() {
var graph = this._graph; const graph = this._graph;
var width = $(graph.element).innerWidth(); const width = $(graph.element).innerWidth();
if (width !== graph.width) { if (width !== graph.width) {
graph.configure({ graph.configure({
width: width, width: width,
...@@ -60,30 +60,32 @@ ...@@ -60,30 +60,32 @@
graph.update(); graph.update();
}; };
BaseGraph.prototype._initGraph = function (element, settings, xSettings, ySettings) { BaseGraph.prototype._initGraph = function(element, settings, xSettings, ySettings) {
var graph = this._graph = new Rickshaw.Graph($.extend({ const graph = this._graph = new Rickshaw.Graph($.extend({
element: element, element: element,
width: $(element).innerWidth(), width: $(element).innerWidth(),
height: this.height, height: this.height,
interpolation: 'linear', interpolation: "linear",
stroke: true stroke: true
}, settings)); },
settings));
this._hoverDetail = new Rickshaw.Graph.HoverDetail({ this._hoverDetail = new Rickshaw.Graph.HoverDetail({
graph: graph, graph: graph,
yFormatter: function (y) { return Math.floor(y); }, yFormatter: function(y) { return Math.floor(y); },
xFormatter: function (x) { return moment(new Date(x * 1000)).format("LLLL"); } xFormatter: function(x) { return moment(new Date(x * 1000)).format("LLLL"); }
}); });
if (xSettings) { if (xSettings) {
this._xAxis = new Rickshaw.Graph.Axis.Time($.extend({ this._xAxis = new Rickshaw.Graph.Axis.Time($.extend({
graph: graph, graph: graph,
timeFixture: new Rickshaw.Fixtures.Time.Local() timeFixture: new Rickshaw.Fixtures.Time.Local()
}, xSettings)); },
xSettings));
var legend = new Rickshaw.Graph.Legend({ const legend = new Rickshaw.Graph.Legend({
element: document.querySelector('#legend'), element: document.querySelector("#legend"),
graph: graph graph: graph
}); });
} }
...@@ -92,16 +94,23 @@ ...@@ -92,16 +94,23 @@
this._yAxis = new Rickshaw.Graph.Axis.Y($.extend({ this._yAxis = new Rickshaw.Graph.Axis.Y($.extend({
graph: graph, graph: graph,
tickFormat: Rickshaw.Fixtures.Number.formatKMBT tickFormat: Rickshaw.Fixtures.Number.formatKMBT
}, ySettings)); },
ySettings));
} }
graph.render(); graph.render();
} };
cap.RealtimeGraph = (function () { cap.RealtimeGraph = (function() {
function RealtimeGraph(element, function RealtimeGraph(element,
pubSucceeded, pubFailed, pubSucceededStr, pubFailedStr, pubSucceeded,
recSucceeded, recFailed, recSucceededStr, recFailedStr pubFailed,
pubSucceededStr,
pubFailedStr,
recSucceeded,
recFailed,
recSucceededStr,
recFailedStr
) { ) {
this._pubSucceeded = pubSucceeded; this._pubSucceeded = pubSucceeded;
this._pubSucceededStr = pubSucceededStr; this._pubSucceededStr = pubSucceededStr;
...@@ -115,48 +124,53 @@ ...@@ -115,48 +124,53 @@
this._recFailed = recFailed; this._recFailed = recFailed;
this._recFailedStr = recFailedStr; this._recFailedStr = recFailedStr;
this._initGraph(element, { this._initGraph(element,
renderer: 'bar', {
renderer: "bar",
series: new Rickshaw.Series.FixedDuration([ series: new Rickshaw.Series.FixedDuration([
{ {
name: pubSucceededStr, name: pubSucceededStr,
color: '#33cc33' color: "#33cc33"
},{ }, {
name: recSucceededStr, name: recSucceededStr,
color: '#3333cc' color: "#3333cc"
},{ }, {
name: pubFailedStr, name: pubFailedStr,
color: '#ff3300' color: "#ff3300"
},{ }, {
name: recFailedStr, name: recFailedStr,
color: '#ff3399' color: "#ff3399"
} }
], ],
undefined, undefined,
{ timeInterval: 2000, maxDataPoints: 100 } { timeInterval: 2000, maxDataPoints: 100 }
) )
}, null, {}); },
null,
{});
} }
RealtimeGraph.prototype = Object.create(BaseGraph.prototype); RealtimeGraph.prototype = Object.create(BaseGraph.prototype);
RealtimeGraph.prototype.appendHistory = function (statistics) { RealtimeGraph.prototype.appendHistory = function(statistics) {
var newPubSucceeded = parseInt(statistics["published_succeeded:count"].intValue); const newPubSucceeded = parseInt(statistics["published_succeeded:count"].intValue);
var newPubFailed = parseInt(statistics["published_failed:count"].intValue); const newPubFailed = parseInt(statistics["published_failed:count"].intValue);
var newRecSucceeded = parseInt(statistics["received_succeeded:count"].intValue); const newRecSucceeded = parseInt(statistics["received_succeeded:count"].intValue);
var newRecFailed = parseInt(statistics["received_failed:count"].intValue); const newRecFailed = parseInt(statistics["received_failed:count"].intValue);
if (this._pubSucceeded !== null && this._pubFailed !== null && if (this._pubSucceeded !== null &&
this._recSucceeded !== null && this._recFailed !== null this._pubFailed !== null &&
this._recSucceeded !== null &&
this._recFailed !== null
) { ) {
var pubSucceeded = newPubSucceeded - this._pubSucceeded; const pubSucceeded = newPubSucceeded - this._pubSucceeded;
var pubFailed = newPubFailed - this._pubFailed; const pubFailed = newPubFailed - this._pubFailed;
var recSucceeded = newRecSucceeded - this._recSucceeded; const recSucceeded = newRecSucceeded - this._recSucceeded;
var recFailed = newRecFailed - this._recFailed; const recFailed = newRecFailed - this._recFailed;
var dataObj = {}; const dataObj = {};
dataObj[this._pubFailedStr] = pubFailed; dataObj[this._pubFailedStr] = pubFailed;
dataObj[this._pubSucceededStr] = pubSucceeded; dataObj[this._pubSucceededStr] = pubSucceeded;
dataObj[this._recFailedStr] = recFailed; dataObj[this._recFailedStr] = recFailed;
...@@ -176,31 +190,41 @@ ...@@ -176,31 +190,41 @@
return RealtimeGraph; return RealtimeGraph;
})(); })();
cap.HistoryGraph = (function () { cap.HistoryGraph = (function() {
function HistoryGraph(element, pubSucceeded, pubFailed, pubSucceededStr, pubFailedStr, function HistoryGraph(element,
recSucceeded, recFailed, recSucceededStr, recFailedStr) { pubSucceeded,
this._initGraph(element, { pubFailed,
renderer: 'area', pubSucceededStr,
pubFailedStr,
recSucceeded,
recFailed,
recSucceededStr,
recFailedStr) {
this._initGraph(element,
{
renderer: "area",
series: [ series: [
{ {
color: '#33cc33', color: "#33cc33",
data: pubSucceeded, data: pubSucceeded,
name: pubSucceededStr name: pubSucceededStr
},{ }, {
color: '#3333cc', color: "#3333cc",
data: recSucceeded, data: recSucceeded,
name: recSucceededStr name: recSucceededStr
},{ }, {
color: '#ff3300', color: "#ff3300",
data: pubFailed, data: pubFailed,
name: pubFailedStr name: pubFailedStr
}, { }, {
color: '#ff3399', color: "#ff3399",
data: recFailed, data: recFailed,
name: recFailedStr name: recFailedStr
} }
] ]
}, {}, { ticksTreatment: 'glow' }); },
{},
{ ticksTreatment: "glow" });
} }
HistoryGraph.prototype = Object.create(BaseGraph.prototype); HistoryGraph.prototype = Object.create(BaseGraph.prototype);
...@@ -208,7 +232,7 @@ ...@@ -208,7 +232,7 @@
return HistoryGraph; return HistoryGraph;
})(); })();
cap.StatisticsPoller = (function () { cap.StatisticsPoller = (function() {
function StatisticsPoller(metricsCallback, statisticsUrl, pollInterval) { function StatisticsPoller(metricsCallback, statisticsUrl, pollInterval) {
this._metricsCallback = metricsCallback; this._metricsCallback = metricsCallback;
this._listeners = []; this._listeners = [];
...@@ -217,12 +241,14 @@ ...@@ -217,12 +241,14 @@
this._intervalId = null; this._intervalId = null;
} }
StatisticsPoller.prototype.start = function () { StatisticsPoller.prototype.start = function() {
var self = this; var self = this;
var intervalFunc = function () { const intervalFunc = function() {
try { try {
$.post(self._statisticsUrl, { metrics: self._metricsCallback() }, function (data) { $.post(self._statisticsUrl,
{ metrics: self._metricsCallback() },
function(data) {
self._notifyListeners(data); self._notifyListeners(data);
}); });
} catch (e) { } catch (e) {
...@@ -233,19 +259,19 @@ ...@@ -233,19 +259,19 @@
this._intervalId = setInterval(intervalFunc, this._pollInterval); this._intervalId = setInterval(intervalFunc, this._pollInterval);
}; };
StatisticsPoller.prototype.stop = function () { StatisticsPoller.prototype.stop = function() {
if (this._intervalId !== null) { if (this._intervalId !== null) {
clearInterval(this._intervalId); clearInterval(this._intervalId);
this._intervalId = null; this._intervalId = null;
} }
}; };
StatisticsPoller.prototype.addListener = function (listener) { StatisticsPoller.prototype.addListener = function(listener) {
this._listeners.push(listener); this._listeners.push(listener);
}; };
StatisticsPoller.prototype._notifyListeners = function (statistics) { StatisticsPoller.prototype._notifyListeners = function(statistics) {
var length = this._listeners.length; const length = this._listeners.length;
var i; var i;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
...@@ -256,36 +282,36 @@ ...@@ -256,36 +282,36 @@
return StatisticsPoller; return StatisticsPoller;
})(); })();
cap.Page = (function () { cap.Page = (function() {
function Page(config) { function Page(config) {
this._metrics = new cap.Metrics(); this._metrics = new cap.Metrics();
var self = this; var self = this;
this._poller = new cap.StatisticsPoller( this._poller = new cap.StatisticsPoller(
function () { return self._metrics.getNames(); }, function() { return self._metrics.getNames(); },
config.pollUrl, config.pollUrl,
config.pollInterval); config.pollInterval);
this._initialize(config.locale); this._initialize(config.locale);
this.realtimeGraph = this._createRealtimeGraph('realtimeGraph'); this.realtimeGraph = this._createRealtimeGraph("realtimeGraph");
this.historyGraph = this._createHistoryGraph('historyGraph'); this.historyGraph = this._createHistoryGraph("historyGraph");
this._poller.start(); this._poller.start();
}; };
Page.prototype._createRealtimeGraph = function (elementId) { Page.prototype._createRealtimeGraph = function(elementId) {
var realtimeElement = document.getElementById(elementId); const realtimeElement = document.getElementById(elementId);
if (realtimeElement) { if (realtimeElement) {
var pubSucceeded = parseInt($(realtimeElement).data('published-succeeded')); const pubSucceeded = parseInt($(realtimeElement).data("published-succeeded"));
var pubFailed = parseInt($(realtimeElement).data('published-failed')); const pubFailed = parseInt($(realtimeElement).data("published-failed"));
var pubSucceededStr = $(realtimeElement).data('published-succeeded-string'); const pubSucceededStr = $(realtimeElement).data("published-succeeded-string");
var pubFailedStr = $(realtimeElement).data('published-failed-string'); const pubFailedStr = $(realtimeElement).data("published-failed-string");
var recSucceeded = parseInt($(realtimeElement).data('received-succeeded')); const recSucceeded = parseInt($(realtimeElement).data("received-succeeded"));
var recFailed = parseInt($(realtimeElement).data('received-failed')); const recFailed = parseInt($(realtimeElement).data("received-failed"));
var recSucceededStr = $(realtimeElement).data('received-succeeded-string'); const recSucceededStr = $(realtimeElement).data("received-succeeded-string");
var recFailedStr = $(realtimeElement).data('received-failed-string'); const recFailedStr = $(realtimeElement).data("received-failed-string");
var realtimeGraph = new Cap.RealtimeGraph(realtimeElement, var realtimeGraph = new Cap.RealtimeGraph(realtimeElement,
pubSucceeded, pubSucceeded,
...@@ -298,11 +324,11 @@ ...@@ -298,11 +324,11 @@
recFailedStr recFailedStr
); );
this._poller.addListener(function (data) { this._poller.addListener(function(data) {
realtimeGraph.appendHistory(data); realtimeGraph.appendHistory(data);
}); });
$(window).resize(function () { $(window).resize(function() {
realtimeGraph.update(); realtimeGraph.update();
}); });
...@@ -312,30 +338,30 @@ ...@@ -312,30 +338,30 @@
return null; return null;
}; };
Page.prototype._createHistoryGraph = function (elementId) { Page.prototype._createHistoryGraph = function(elementId) {
var historyElement = document.getElementById(elementId); const historyElement = document.getElementById(elementId);
if (historyElement) { if (historyElement) {
var createSeries = function (obj) { const createSeries = function(obj) {
var series = []; const series = [];
for (var date in obj) { for (let date in obj) {
if (obj.hasOwnProperty(date)) { if (obj.hasOwnProperty(date)) {
var value = obj[date]; const value = obj[date];
var point = { x: Date.parse(date) / 1000, y: value }; const point = { x: Date.parse(date) / 1000, y: value };
series.unshift(point); series.unshift(point);
} }
} }
return series; return series;
}; };
var publishedSucceeded = createSeries($(historyElement).data("published-succeeded")); const publishedSucceeded = createSeries($(historyElement).data("published-succeeded"));
var publishedFailed = createSeries($(historyElement).data("published-failed")); const publishedFailed = createSeries($(historyElement).data("published-failed"));
var publishedSucceededStr = $(historyElement).data('published-succeeded-string'); const publishedSucceededStr = $(historyElement).data("published-succeeded-string");
var publishedFailedStr = $(historyElement).data('published-failed-string'); const publishedFailedStr = $(historyElement).data("published-failed-string");
var receivedSucceeded = createSeries($(historyElement).data("received-succeeded")); const receivedSucceeded = createSeries($(historyElement).data("received-succeeded"));
var receivedFailed = createSeries($(historyElement).data("received-failed")); const receivedFailed = createSeries($(historyElement).data("received-failed"));
var receivedSucceededStr = $(historyElement).data('received-succeeded-string'); const receivedSucceededStr = $(historyElement).data("received-succeeded-string");
var receivedFailedStr = $(historyElement).data('received-failed-string'); const receivedFailedStr = $(historyElement).data("received-failed-string");
var historyGraph = new Cap.HistoryGraph(historyElement, var historyGraph = new Cap.HistoryGraph(historyElement,
publishedSucceeded, publishedSucceeded,
...@@ -348,7 +374,7 @@ ...@@ -348,7 +374,7 @@
receivedFailedStr, receivedFailedStr,
); );
$(window).resize(function () { $(window).resize(function() {
historyGraph.update(); historyGraph.update();
}); });
...@@ -358,39 +384,39 @@ ...@@ -358,39 +384,39 @@
return null; return null;
}; };
Page.prototype._initialize = function (locale) { Page.prototype._initialize = function(locale) {
moment.locale(locale); moment.locale(locale);
var updateRelativeDates = function () { const updateRelativeDates = function() {
$('*[data-moment]').each(function () { $("*[data-moment]").each(function() {
var $this = $(this); const $this = $(this);
var timestamp = $this.data('moment'); const timestamp = $this.data("moment");
if (timestamp) { if (timestamp) {
var time = moment(timestamp, 'X'); const time = moment(timestamp, "X");
$this.html(time.fromNow()) $this.html(time.fromNow())
.attr('title', time.format('llll')) .attr("title", time.format("llll"))
.attr('data-container', 'body'); .attr("data-container", "body");
} }
}); });
$('*[data-moment-title]').each(function () { $("*[data-moment-title]").each(function() {
var $this = $(this); const $this = $(this);
var timestamp = $this.data('moment-title'); const timestamp = $this.data("moment-title");
if (timestamp) { if (timestamp) {
var time = moment(timestamp, 'X'); const time = moment(timestamp, "X");
$this.prop('title', time.format('llll')) $this.prop("title", time.format("llll"))
.attr('data-container', 'body'); .attr("data-container", "body");
} }
}); });
$('*[data-moment-local]').each(function () { $("*[data-moment-local]").each(function() {
var $this = $(this); const $this = $(this);
var timestamp = $this.data('moment-local'); const timestamp = $this.data("moment-local");
if (timestamp) { if (timestamp) {
var time = moment(timestamp, 'X'); const time = moment(timestamp, "X");
$this.html(time.format('l LTS')); $this.html(time.format("l LTS"));
} }
}); });
}; };
...@@ -401,40 +427,44 @@ ...@@ -401,40 +427,44 @@
$("*[title]").tooltip(); $("*[title]").tooltip();
var self = this; var self = this;
$("*[data-metric]").each(function () { $("*[data-metric]").each(function() {
var name = $(this).data('metric'); const name = $(this).data("metric");
self._metrics.addElement(name, this); self._metrics.addElement(name, this);
}); });
this._poller.addListener(function (metrics) { this._poller.addListener(function(metrics) {
for (var name in metrics) { for (let name in metrics) {
var elements = self._metrics.getElements(name); const elements = self._metrics.getElements(name);
for (var i = 0; i < elements.length; i++) { for (let i = 0; i < elements.length; i++) {
var metric = metrics[name]; const metric = metrics[name];
var metricClass = metric ? "metric-" + metric.style : "metric-null"; const metricClass = metric ? `metric-${metric.style}` : "metric-null";
var highlighted = metric && metric.highlighted ? "highlighted" : null; const highlighted = metric && metric.highlighted ? "highlighted" : null;
var value = metric ? metric.value : null; const value = metric ? metric.value : null;
$(elements[i]) $(elements[i])
.text(value) .text(value)
.closest('.metric') .closest(".metric")
.removeClass() .removeClass()
.addClass(["metric", metricClass, highlighted].join(' ')); .addClass(["metric", metricClass, highlighted].join(" "));
} }
} }
}); });
$(document).on('click', '*[data-ajax]', function (e) { $(document).on("click",
"*[data-ajax]",
function(e) {
var $this = $(this); var $this = $(this);
var confirmText = $this.data('confirm'); const confirmText = $this.data("confirm");
if (!confirmText || confirm(confirmText)) { if (!confirmText || confirm(confirmText)) {
$this.prop('disabled'); $this.prop("disabled");
var loadingDelay = setTimeout(function () { var loadingDelay = setTimeout(function() {
$this.button('loading'); $this.button("loading");
}, 100); },
100);
$.post($this.data('ajax'), function () {
$.post($this.data("ajax"),
function() {
clearTimeout(loadingDelay); clearTimeout(loadingDelay);
window.location.reload(); window.location.reload();
}); });
...@@ -443,114 +473,127 @@ ...@@ -443,114 +473,127 @@
e.preventDefault(); e.preventDefault();
}); });
$(document).on('click', '.expander', function (e) { $(document).on("click",
".expander",
function(e) {
var $expander = $(this), var $expander = $(this),
$expandable = $expander.closest('tr').next().find('.expandable'); $expandable = $expander.closest("tr").next().find(".expandable");
if (!$expandable.is(':visible')) { if (!$expandable.is(":visible")) {
$expander.text('Less details...'); $expander.text("Less details...");
} }
$expandable.slideToggle( $expandable.slideToggle(
150, 150,
function () { function() {
if (!$expandable.is(':visible')) { if (!$expandable.is(":visible")) {
$expander.text('More details...'); $expander.text("More details...");
} }
}); });
e.preventDefault(); e.preventDefault();
}); });
$('.js-jobs-list').each(function () { $(".js-jobs-list").each(function() {
var container = this; var container = this;
var selectRow = function (row, isSelected) { var selectRow = function(row, isSelected) {
var $checkbox = $('.js-jobs-list-checkbox', row); const $checkbox = $(".js-jobs-list-checkbox", row);
if ($checkbox.length > 0) { if ($checkbox.length > 0) {
$checkbox.prop('checked', isSelected); $checkbox.prop("checked", isSelected);
$(row).toggleClass('highlight', isSelected); $(row).toggleClass("highlight", isSelected);
} }
}; };
var toggleRowSelection = function (row) { var toggleRowSelection = function(row) {
var $checkbox = $('.js-jobs-list-checkbox', row); const $checkbox = $(".js-jobs-list-checkbox", row);
if ($checkbox.length > 0) { if ($checkbox.length > 0) {
var isSelected = $checkbox.is(':checked'); const isSelected = $checkbox.is(":checked");
selectRow(row, !isSelected); selectRow(row, !isSelected);
} }
}; };
var setListState = function (state) { var setListState = function(state) {
$('.js-jobs-list-select-all', container) $(".js-jobs-list-select-all", container)
.prop('checked', state === 'all-selected') .prop("checked", state === "all-selected")
.prop('indeterminate', state === 'some-selected'); .prop("indeterminate", state === "some-selected");
$('.js-jobs-list-command', container) $(".js-jobs-list-command", container)
.prop('disabled', state === 'none-selected'); .prop("disabled", state === "none-selected");
}; };
var updateListState = function () { var updateListState = function() {
var selectedRows = $('.js-jobs-list-checkbox', container).map(function () { const selectedRows = $(".js-jobs-list-checkbox", container).map(function() {
return $(this).prop('checked'); return $(this).prop("checked");
}).get(); }).get();
var state = 'none-selected'; var state = "none-selected";
if (selectedRows.length > 0) { if (selectedRows.length > 0) {
state = 'some-selected'; state = "some-selected";
if ($.inArray(false, selectedRows) === -1) { if ($.inArray(false, selectedRows) === -1) {
state = 'all-selected'; state = "all-selected";
} else if ($.inArray(true, selectedRows) === -1) { } else if ($.inArray(true, selectedRows) === -1) {
state = 'none-selected'; state = "none-selected";
} }
} }
setListState(state); setListState(state);
}; };
$(this).on('click', '.js-jobs-list-checkbox', function (e) { $(this).on("click",
".js-jobs-list-checkbox",
function(e) {
selectRow( selectRow(
$(this).closest('.js-jobs-list-row').first(), $(this).closest(".js-jobs-list-row").first(),
$(this).is(':checked')); $(this).is(":checked"));
updateListState(); updateListState();
e.stopPropagation(); e.stopPropagation();
}); });
$(this).on('click', '.js-jobs-list-row', function (e) { $(this).on("click",
if ($(e.target).is('a')) return; ".js-jobs-list-row",
function(e) {
if ($(e.target).is("a")) return;
toggleRowSelection(this); toggleRowSelection(this);
updateListState(); updateListState();
}); });
$(this).on('click', '.js-jobs-list-select-all', function () { $(this).on("click",
var selectRows = $(this).is(':checked'); ".js-jobs-list-select-all",
function() {
var selectRows = $(this).is(":checked");
$('.js-jobs-list-row', container).each(function () { $(".js-jobs-list-row", container).each(function() {
selectRow(this, selectRows); selectRow(this, selectRows);
}); });
updateListState(); updateListState();
}); });
$(this).on('click', '.js-jobs-list-command', function (e) { $(this).on("click",
".js-jobs-list-command",
function(e) {
var $this = $(this); var $this = $(this);
var confirmText = $this.data('confirm'); const confirmText = $this.data("confirm");
var jobs = $("input[name='messages[]']:checked", container).map(function () { const jobs = $("input[name='messages[]']:checked", container).map(function() {
return $(this).val(); return $(this).val();
}).get(); }).get();
if (!confirmText || confirm(confirmText)) { if (!confirmText || confirm(confirmText)) {
$this.prop('disabled'); $this.prop("disabled");
var loadingDelay = setTimeout(function () { var loadingDelay = setTimeout(function() {
$this.button('loading'); $this.button("loading");
}, 100); },
100);
$.post($this.data('url'), { 'messages[]': jobs }, function () {
$.post($this.data("url"),
{ 'messages[]': jobs },
function() {
clearTimeout(loadingDelay); clearTimeout(loadingDelay);
window.location.reload(); window.location.reload();
}); });
...@@ -567,20 +610,20 @@ ...@@ -567,20 +610,20 @@
})(); })();
})(window.Cap = window.Cap || {}); })(window.Cap = window.Cap || {});
$(function () { $(function() {
Cap.page = new Cap.Page(Cap.config); Cap.page = new Cap.Page(Cap.config);
}); });
(function () { (function() {
var json = null; var json = null;
$(".openModal").click(function () { $(".openModal").click(function() {
var url = $(this).data("url"); const url = $(this).data("url");
$.ajax({ $.ajax({
url: url, url: url,
dataType: "json", dataType: "json",
success: function (data) { success: function(data) {
json = data; json = data;
$("#formatBtn").click(); $("#formatBtn").click();
$(".modal").modal("show"); $(".modal").modal("show");
...@@ -588,25 +631,25 @@ $(function () { ...@@ -588,25 +631,25 @@ $(function () {
}); });
}); });
$("#formatBtn").click(function () { $("#formatBtn").click(function() {
$('#jsonContent').JSONView(json); $("#jsonContent").JSONView(json);
}); });
$("#rawBtn").click(function () { $("#rawBtn").click(function() {
$('#jsonContent').text(JSON.stringify(json)); $("#jsonContent").text(JSON.stringify(json));
}); });
$("#expandBtn").click(function () { $("#expandBtn").click(function() {
$('#jsonContent').JSONView('expand'); $("#jsonContent").JSONView("expand");
}); });
$("#collapseBtn").click(function () { $("#collapseBtn").click(function() {
$('#jsonContent').JSONView('collapse'); $("#jsonContent").JSONView("collapse");
}); });
})(); })();
function nodeSwitch(id) { function nodeSwitch(id) {
console.log("id:" + id); console.log(`id:${id}`);
document.cookie = "cap.node=" + escape(id) + ";"; document.cookie = `cap.node=${escape(id)};`;
} }
\ No newline at end of file
...@@ -11,42 +11,6 @@ namespace DotNetCore.CAP.Dashboard ...@@ -11,42 +11,6 @@ namespace DotNetCore.CAP.Dashboard
{ {
private static readonly Dictionary<string, DashboardMetric> Metrics = new Dictionary<string, DashboardMetric>(); private static readonly Dictionary<string, DashboardMetric> Metrics = new Dictionary<string, DashboardMetric>();
static DashboardMetrics()
{
AddMetric(ServerCount);
AddMetric(SubscriberCount);
AddMetric(PublishedFailedCountOrNull);
AddMetric(ReceivedFailedCountOrNull);
AddMetric(PublishedProcessingCount);
AddMetric(ReceivedProcessingCount);
AddMetric(PublishedSucceededCount);
AddMetric(ReceivedSucceededCount);
AddMetric(PublishedFailedCount);
AddMetric(ReceivedFailedCount);
}
public static void AddMetric(DashboardMetric metric)
{
if (metric == null) throw new ArgumentNullException(nameof(metric));
lock (Metrics)
{
Metrics[metric.Name] = metric;
}
}
public static IEnumerable<DashboardMetric> GetMetrics()
{
lock (Metrics)
{
return Metrics.Values.ToList();
}
}
public static readonly DashboardMetric ServerCount = new DashboardMetric( public static readonly DashboardMetric ServerCount = new DashboardMetric(
"servers:count", "servers:count",
"Metrics_Servers", "Metrics_Servers",
...@@ -156,5 +120,41 @@ namespace DotNetCore.CAP.Dashboard ...@@ -156,5 +120,41 @@ namespace DotNetCore.CAP.Dashboard
Style = page.Statistics.ReceivedFailed > 0 ? MetricStyle.Danger : MetricStyle.Default, Style = page.Statistics.ReceivedFailed > 0 ? MetricStyle.Danger : MetricStyle.Default,
Highlighted = page.Statistics.ReceivedFailed > 0 Highlighted = page.Statistics.ReceivedFailed > 0
}); });
static DashboardMetrics()
{
AddMetric(ServerCount);
AddMetric(SubscriberCount);
AddMetric(PublishedFailedCountOrNull);
AddMetric(ReceivedFailedCountOrNull);
AddMetric(PublishedProcessingCount);
AddMetric(ReceivedProcessingCount);
AddMetric(PublishedSucceededCount);
AddMetric(ReceivedSucceededCount);
AddMetric(PublishedFailedCount);
AddMetric(ReceivedFailedCount);
}
public static void AddMetric(DashboardMetric metric)
{
if (metric == null) throw new ArgumentNullException(nameof(metric));
lock (Metrics)
{
Metrics[metric.Name] = metric;
}
}
public static IEnumerable<DashboardMetric> GetMetrics()
{
lock (Metrics)
{
return Metrics.Values.ToList();
}
}
} }
} }
\ No newline at end of file
...@@ -35,7 +35,10 @@ namespace DotNetCore.CAP.Dashboard ...@@ -35,7 +35,10 @@ namespace DotNetCore.CAP.Dashboard
public override string LocalIpAddress => _context.Connection.LocalIpAddress.ToString(); public override string LocalIpAddress => _context.Connection.LocalIpAddress.ToString();
public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.ToString(); public override string RemoteIpAddress => _context.Connection.RemoteIpAddress.ToString();
public override string GetQuery(string key) => _context.Request.Query[key]; public override string GetQuery(string key)
{
return _context.Request.Query[key];
}
public override async Task<IList<string>> GetFormValuesAsync(string key) public override async Task<IList<string>> GetFormValuesAsync(string key)
{ {
......
...@@ -7,8 +7,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -7,8 +7,8 @@ namespace DotNetCore.CAP.Dashboard
internal class EmbeddedResourceDispatcher : IDashboardDispatcher internal class EmbeddedResourceDispatcher : IDashboardDispatcher
{ {
private readonly Assembly _assembly; private readonly Assembly _assembly;
private readonly string _resourceName;
private readonly string _contentType; private readonly string _contentType;
private readonly string _resourceName;
public EmbeddedResourceDispatcher( public EmbeddedResourceDispatcher(
string contentType, string contentType,
...@@ -47,9 +47,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -47,9 +47,8 @@ namespace DotNetCore.CAP.Dashboard
using (var inputStream = assembly.GetManifestResourceStream(resourceName)) using (var inputStream = assembly.GetManifestResourceStream(resourceName))
{ {
if (inputStream == null) if (inputStream == null)
{ throw new ArgumentException(
throw new ArgumentException($@"Resource with name {resourceName} not found in assembly {assembly}."); $@"Resource with name {resourceName} not found in assembly {assembly}.");
}
inputStream.CopyTo(response.Body); inputStream.CopyTo(response.Body);
} }
......
...@@ -16,16 +16,14 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -16,16 +16,14 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
public class GatewayProxyMiddleware public class GatewayProxyMiddleware
{ {
public const string NodeCookieName = "cap.node"; public const string NodeCookieName = "cap.node";
private readonly ILogger _logger;
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly IRequestMapper _requestMapper;
private readonly IHttpRequester _requester; private readonly IHttpRequester _requester;
private readonly IRequestMapper _requestMapper;
private INodeDiscoveryProvider _discoveryProvider; private INodeDiscoveryProvider _discoveryProvider;
protected HttpRequestMessage DownstreamRequest { get; set; }
public GatewayProxyMiddleware(RequestDelegate next, public GatewayProxyMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IRequestMapper requestMapper, IRequestMapper requestMapper,
...@@ -37,6 +35,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -37,6 +35,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
_requester = requester; _requester = requester;
} }
protected HttpRequestMessage DownstreamRequest { get; set; }
public async Task Invoke(HttpContext context, public async Task Invoke(HttpContext context,
DiscoveryOptions discoveryOptions, DiscoveryOptions discoveryOptions,
INodeDiscoveryProvider discoveryProvider) INodeDiscoveryProvider discoveryProvider)
...@@ -53,7 +53,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -53,7 +53,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
else else
{ {
//For performance reasons, we need to put this functionality in the else //For performance reasons, we need to put this functionality in the else
var isSwitchNode = request.Cookies.TryGetValue(NodeCookieName, out string requestNodeId); var isSwitchNode = request.Cookies.TryGetValue(NodeCookieName, out var requestNodeId);
var isCurrentNode = discoveryOptions.NodeId.ToString() == requestNodeId; var isCurrentNode = discoveryOptions.NodeId.ToString() == requestNodeId;
var isNodesPage = request.Path.StartsWithSegments(new PathString(pathMatch + "/nodes")); var isNodesPage = request.Path.StartsWithSegments(new PathString(pathMatch + "/nodes"));
...@@ -65,7 +65,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -65,7 +65,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
{ {
_logger.LogDebug("started calling gateway proxy middleware"); _logger.LogDebug("started calling gateway proxy middleware");
if (TryGetRemoteNode(requestNodeId, out Node node)) if (TryGetRemoteNode(requestNodeId, out var node))
{ {
try try
{ {
...@@ -94,33 +94,28 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -94,33 +94,28 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
public async Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response) public async Task SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
{ {
foreach (var httpResponseHeader in response.Content.Headers) foreach (var httpResponseHeader in response.Content.Headers)
{
AddHeaderIfDoesntExist(context, httpResponseHeader); AddHeaderIfDoesntExist(context, httpResponseHeader);
}
var content = await response.Content.ReadAsByteArrayAsync(); var content = await response.Content.ReadAsByteArrayAsync();
AddHeaderIfDoesntExist(context, AddHeaderIfDoesntExist(context,
new KeyValuePair<string, IEnumerable<string>>("Content-Length", new[] { content.Length.ToString() })); new KeyValuePair<string, IEnumerable<string>>("Content-Length", new[] {content.Length.ToString()}));
context.Response.OnStarting(state => context.Response.OnStarting(state =>
{ {
var httpContext = (HttpContext)state; var httpContext = (HttpContext) state;
httpContext.Response.StatusCode = (int)response.StatusCode; httpContext.Response.StatusCode = (int) response.StatusCode;
return Task.CompletedTask; return Task.CompletedTask;
}, context); }, context);
using (Stream stream = new MemoryStream(content)) using (Stream stream = new MemoryStream(content))
{ {
if (response.StatusCode != HttpStatusCode.NotModified) if (response.StatusCode != HttpStatusCode.NotModified)
{
await stream.CopyToAsync(context.Response.Body); await stream.CopyToAsync(context.Response.Body);
} }
} }
}
private bool TryGetRemoteNode(string requestNodeId, out Node node) private bool TryGetRemoteNode(string requestNodeId, out Node node)
{ {
...@@ -139,10 +134,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -139,10 +134,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
KeyValuePair<string, IEnumerable<string>> httpResponseHeader) KeyValuePair<string, IEnumerable<string>> httpResponseHeader)
{ {
if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key)) if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key))
{
context.Response.Headers.Add(httpResponseHeader.Key, context.Response.Headers.Add(httpResponseHeader.Key,
new StringValues(httpResponseHeader.Value.ToArray())); new StringValues(httpResponseHeader.Value.ToArray()));
} }
} }
}
} }
\ No newline at end of file
...@@ -12,14 +12,14 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -12,14 +12,14 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
{ {
public class RequestMapper : IRequestMapper public class RequestMapper : IRequestMapper
{ {
private readonly string[] _unsupportedHeaders = { "host", "cookie" };
private const string SchemeDelimiter = "://"; private const string SchemeDelimiter = "://";
private readonly string[] _unsupportedHeaders = {"host", "cookie"};
public async Task<HttpRequestMessage> Map(HttpRequest request) public async Task<HttpRequestMessage> Map(HttpRequest request)
{ {
try try
{ {
var requestMessage = new HttpRequestMessage() var requestMessage = new HttpRequestMessage
{ {
Content = await MapContent(request), Content = await MapContent(request),
Method = MapMethod(request), Method = MapMethod(request),
...@@ -45,11 +45,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -45,11 +45,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
FragmentString fragment = new FragmentString()) FragmentString fragment = new FragmentString())
{ {
if (scheme == null) if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme)); throw new ArgumentNullException(nameof(scheme));
}
var combinedPath = (pathBase.HasValue || path.HasValue) ? (pathBase + path).ToString() : "/"; var combinedPath = pathBase.HasValue || path.HasValue ? (pathBase + path).ToString() : "/";
var encodedHost = host.ToString(); var encodedHost = host.ToString();
var encodedQuery = query.ToString(); var encodedQuery = query.ToString();
...@@ -77,13 +75,11 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -77,13 +75,11 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
private async Task<HttpContent> MapContent(HttpRequest request) private async Task<HttpContent> MapContent(HttpRequest request)
{ {
if (request.Body == null) if (request.Body == null)
{
return null; return null;
}
var content = new ByteArrayContent(await ToByteArray(request.Body)); var content = new ByteArrayContent(await ToByteArray(request.Body));
content.Headers.TryAddWithoutValidation("Content-Type", new[] { request.ContentType }); content.Headers.TryAddWithoutValidation("Content-Type", new[] {request.ContentType});
return content; return content;
} }
...@@ -101,13 +97,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy ...@@ -101,13 +97,9 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy
private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage)
{ {
foreach (var header in request.Headers) foreach (var header in request.Headers)
{
if (IsSupportedHeader(header)) if (IsSupportedHeader(header))
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
} }
}
}
private async Task<byte[]> ToByteArray(Stream stream) private async Task<byte[]> ToByteArray(Stream stream)
{ {
......
namespace DotNetCore.CAP.Dashboard.GatewayProxy using System.Net.Http;
{ using System.Threading.Tasks;
using System.Net.Http; using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace DotNetCore.CAP.Dashboard.GatewayProxy
{
public interface IRequestMapper public interface IRequestMapper
{ {
Task<HttpRequestMessage> Map(HttpRequest request); Task<HttpRequestMessage> Map(HttpRequest request);
......
...@@ -8,7 +8,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -8,7 +8,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
{ {
internal class HttpClientBuilder : IHttpClientBuilder internal class HttpClientBuilder : IHttpClientBuilder
{ {
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>(); private readonly Dictionary<int, Func<DelegatingHandler>> _handlers =
new Dictionary<int, Func<DelegatingHandler>>();
public IHttpClient Create() public IHttpClient Create()
{ {
...@@ -41,13 +42,13 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -41,13 +42,13 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
/// </summary> /// </summary>
internal class HttpClientWrapper : IHttpClient internal class HttpClientWrapper : IHttpClient
{ {
public HttpClient Client { get; }
public HttpClientWrapper(HttpClient client) public HttpClientWrapper(HttpClient client)
{ {
Client = client; Client = client;
} }
public HttpClient Client { get; }
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{ {
return Client.SendAsync(request); return Client.SendAsync(request);
......
...@@ -44,15 +44,13 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -44,15 +44,13 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
var httpClient = _cacheHandlers.Get(cacheKey); var httpClient = _cacheHandlers.Get(cacheKey);
if (httpClient == null) if (httpClient == null)
{
httpClient = builder.Create(); httpClient = builder.Create();
}
return httpClient; return httpClient;
} }
private string GetCacheKey(HttpRequestMessage request, IHttpClientBuilder builder) private string GetCacheKey(HttpRequestMessage request, IHttpClientBuilder builder)
{ {
string baseUrl = $"{request.RequestUri.Scheme}://{request.RequestUri.Authority}"; var baseUrl = $"{request.RequestUri.Scheme}://{request.RequestUri.Authority}";
return baseUrl; return baseUrl;
} }
......
...@@ -5,7 +5,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -5,7 +5,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
public interface IHttpClientBuilder public interface IHttpClientBuilder
{ {
/// <summary> /// <summary>
/// Creates the <see cref="HttpClient"/> /// Creates the <see cref="HttpClient" />
/// </summary> /// </summary>
IHttpClient Create(); IHttpClient Create();
} }
......
...@@ -5,7 +5,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -5,7 +5,8 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
{ {
public class MemoryHttpClientCache : IHttpClientCache public class MemoryHttpClientCache : IHttpClientCache
{ {
private readonly ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>> _httpClientsCache = new ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>>(); private readonly ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>> _httpClientsCache =
new ConcurrentDictionary<string, ConcurrentQueue<IHttpClient>>();
public void Set(string id, IHttpClient client, TimeSpan expirationTime) public void Set(string id, IHttpClient client, TimeSpan expirationTime)
{ {
...@@ -30,9 +31,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester ...@@ -30,9 +31,7 @@ namespace DotNetCore.CAP.Dashboard.GatewayProxy.Requester
{ {
IHttpClient client = null; IHttpClient client = null;
if (_httpClientsCache.TryGetValue(id, out var connectionQueue)) if (_httpClientsCache.TryGetValue(id, out var connectionQueue))
{
connectionQueue.TryDequeue(out client); connectionQueue.TryDequeue(out client);
}
return client; return client;
} }
......
...@@ -31,9 +31,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -31,9 +31,7 @@ namespace DotNetCore.CAP.Dashboard
public NonEscapedString MessagesSidebar(MessageType type) public NonEscapedString MessagesSidebar(MessageType type)
{ {
if (type == MessageType.Publish) if (type == MessageType.Publish)
{
return SidebarMenu(MessagesSidebarMenu.PublishedItems); return SidebarMenu(MessagesSidebarMenu.PublishedItems);
}
return SidebarMenu(MessagesSidebarMenu.ReceivedItems); return SidebarMenu(MessagesSidebarMenu.ReceivedItems);
} }
...@@ -80,12 +78,11 @@ namespace DotNetCore.CAP.Dashboard ...@@ -80,12 +78,11 @@ namespace DotNetCore.CAP.Dashboard
public NonEscapedString StateLabel(string stateName) public NonEscapedString StateLabel(string stateName)
{ {
if (String.IsNullOrWhiteSpace(stateName)) if (string.IsNullOrWhiteSpace(stateName))
{
return Raw($"<em>{Strings.Common_NoState}</em>"); return Raw($"<em>{Strings.Common_NoState}</em>");
}
return Raw($"<span class=\"label label-default\" style=\"background-color: {MessageHistoryRenderer.GetForegroundStateColor(stateName)};\">{stateName}</span>"); return Raw(
$"<span class=\"label label-default\" style=\"background-color: {MessageHistoryRenderer.GetForegroundStateColor(stateName)};\">{stateName}</span>");
} }
public NonEscapedString RelativeTime(DateTime value) public NonEscapedString RelativeTime(DateTime value)
...@@ -109,52 +106,36 @@ namespace DotNetCore.CAP.Dashboard ...@@ -109,52 +106,36 @@ namespace DotNetCore.CAP.Dashboard
var builder = new StringBuilder(); var builder = new StringBuilder();
if (displaySign) if (displaySign)
{
builder.Append(duration.Value.TotalMilliseconds < 0 ? "-" : "+"); builder.Append(duration.Value.TotalMilliseconds < 0 ? "-" : "+");
}
duration = duration.Value.Duration(); duration = duration.Value.Duration();
if (duration.Value.Days > 0) if (duration.Value.Days > 0)
{
builder.Append($"{duration.Value.Days}d "); builder.Append($"{duration.Value.Days}d ");
}
if (duration.Value.Hours > 0) if (duration.Value.Hours > 0)
{
builder.Append($"{duration.Value.Hours}h "); builder.Append($"{duration.Value.Hours}h ");
}
if (duration.Value.Minutes > 0) if (duration.Value.Minutes > 0)
{
builder.Append($"{duration.Value.Minutes}m "); builder.Append($"{duration.Value.Minutes}m ");
}
if (duration.Value.TotalHours < 1) if (duration.Value.TotalHours < 1)
{
if (duration.Value.Seconds > 0) if (duration.Value.Seconds > 0)
{ {
builder.Append(duration.Value.Seconds); builder.Append(duration.Value.Seconds);
if (duration.Value.Milliseconds > 0) if (duration.Value.Milliseconds > 0)
{
builder.Append($".{duration.Value.Milliseconds.ToString().PadLeft(3, '0')}"); builder.Append($".{duration.Value.Milliseconds.ToString().PadLeft(3, '0')}");
}
builder.Append("s "); builder.Append("s ");
} }
else else
{ {
if (duration.Value.Milliseconds > 0) if (duration.Value.Milliseconds > 0)
{
builder.Append($"{duration.Value.Milliseconds}ms "); builder.Append($"{duration.Value.Milliseconds}ms ");
} }
}
}
if (builder.Length <= 1) if (builder.Length <= 1)
{
builder.Append(" <1ms "); builder.Append(" <1ms ");
}
builder.Remove(builder.Length - 1, 1); builder.Remove(builder.Length - 1, 1);
...@@ -163,7 +144,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -163,7 +144,7 @@ namespace DotNetCore.CAP.Dashboard
public string FormatProperties(IDictionary<string, string> properties) public string FormatProperties(IDictionary<string, string> properties)
{ {
return String.Join(", ", properties.Select(x => $"{x.Key}: \"{x.Value}\"")); return string.Join(", ", properties.Select(x => $"{x.Key}: \"{x.Value}\""));
} }
public NonEscapedString QueueLabel(string queue) public NonEscapedString QueueLabel(string queue)
...@@ -179,7 +160,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -179,7 +160,7 @@ namespace DotNetCore.CAP.Dashboard
{ {
var parts = serverId.Split(':'); var parts = serverId.Split(':');
var shortenedId = parts.Length > 1 var shortenedId = parts.Length > 1
? String.Join(":", parts.Take(parts.Length - 1)) ? string.Join(":", parts.Take(parts.Length - 1))
: serverId; : serverId;
return new NonEscapedString( return new NonEscapedString(
...@@ -188,20 +169,40 @@ namespace DotNetCore.CAP.Dashboard ...@@ -188,20 +169,40 @@ namespace DotNetCore.CAP.Dashboard
public NonEscapedString NodeSwitchLink(string id) public NonEscapedString NodeSwitchLink(string id)
{ {
return Raw($"<a class=\"job-method\" onclick=\"nodeSwitch({id});\" href=\"javascript:;\">{Strings.NodePage_Switch}</a>"); return Raw(
$"<a class=\"job-method\" onclick=\"nodeSwitch({id});\" href=\"javascript:;\">{Strings.NodePage_Switch}</a>");
}
public NonEscapedString StackTrace(string stackTrace)
{
try
{
//return new NonEscapedString(StackTraceFormatter.FormatHtml(stackTrace, StackTraceHtmlFragments));
return new NonEscapedString(stackTrace);
}
catch (RegexMatchTimeoutException)
{
return new NonEscapedString(HtmlEncode(stackTrace));
}
}
public string HtmlEncode(string text)
{
return WebUtility.HtmlEncode(text);
} }
#region MethodEscaped #region MethodEscaped
public NonEscapedString MethodEscaped(MethodInfo method) public NonEscapedString MethodEscaped(MethodInfo method)
{ {
var @public = WrapKeyword("public"); var @public = WrapKeyword("public");
var @async = string.Empty; var async = string.Empty;
string @return; string @return;
var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(method.ReturnType, out var coercedAwaitableInfo); var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(method.ReturnType, out var coercedAwaitableInfo);
if (isAwaitable) if (isAwaitable)
{ {
@async = WrapKeyword("async"); async = WrapKeyword("async");
var asyncResultType = coercedAwaitableInfo.AwaitableInfo.ResultType; var asyncResultType = coercedAwaitableInfo.AwaitableInfo.ResultType;
@return = WrapType("Task") + WrapIdentifier("<") + WrapType(asyncResultType) + WrapIdentifier(">"); @return = WrapType("Task") + WrapIdentifier("<") + WrapType(asyncResultType) + WrapIdentifier(">");
...@@ -211,7 +212,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -211,7 +212,7 @@ namespace DotNetCore.CAP.Dashboard
@return = WrapType(method.ReturnType); @return = WrapType(method.ReturnType);
} }
var @name = method.Name; var name = method.Name;
string paramType = null; string paramType = null;
string paramName = null; string paramName = null;
...@@ -227,7 +228,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -227,7 +228,8 @@ namespace DotNetCore.CAP.Dashboard
var paramString = paramType == null ? "();" : $"({paramType} {paramName});"; var paramString = paramType == null ? "();" : $"({paramType} {paramName});";
var outputString = @public + " " + (string.IsNullOrEmpty(@async) ? "" : @async + " ") + @return + " " + @name + paramString; var outputString = @public + " " + (string.IsNullOrEmpty(async) ? "" : async + " ") + @return + " " + name +
paramString;
return new NonEscapedString(outputString); return new NonEscapedString(outputString);
} }
...@@ -235,27 +237,16 @@ namespace DotNetCore.CAP.Dashboard ...@@ -235,27 +237,16 @@ namespace DotNetCore.CAP.Dashboard
private string WrapType(Type type) private string WrapType(Type type)
{ {
if (type == null) if (type == null)
{
return string.Empty; return string.Empty;
}
if (type.Name == "Void") if (type.Name == "Void")
{
return WrapKeyword(type.Name.ToLower()); return WrapKeyword(type.Name.ToLower());
}
if (Helper.IsComplexType(type)) if (Helper.IsComplexType(type))
{
return WrapType(type.Name); return WrapType(type.Name);
}
if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal))
{
return WrapKeyword(type.Name.ToLower()); return WrapKeyword(type.Name.ToLower());
}
else
{
return WrapType(type.Name); return WrapType(type.Name);
} }
}
private string WrapIdentifier(string value) private string WrapIdentifier(string value)
{ {
...@@ -276,24 +267,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -276,24 +267,7 @@ namespace DotNetCore.CAP.Dashboard
{ {
return $"<span class=\"{@class}\">{value}</span>"; return $"<span class=\"{@class}\">{value}</span>";
} }
#endregion
public NonEscapedString StackTrace(string stackTrace)
{
try
{
//return new NonEscapedString(StackTraceFormatter.FormatHtml(stackTrace, StackTraceHtmlFragments));
return new NonEscapedString(stackTrace);
}
catch (RegexMatchTimeoutException)
{
return new NonEscapedString(HtmlEncode(stackTrace));
}
}
public string HtmlEncode(string text) #endregion
{
return WebUtility.HtmlEncode(text);
}
} }
} }
\ No newline at end of file
...@@ -26,20 +26,18 @@ namespace DotNetCore.CAP.Dashboard ...@@ -26,20 +26,18 @@ namespace DotNetCore.CAP.Dashboard
string serialized = null; string serialized = null;
if (_command != null) if (_command != null)
{ {
object result = _command(context); var result = _command(context);
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
{ {
ContractResolver = new CamelCasePropertyNamesContractResolver(), ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new JsonConverter[] { new StringEnumConverter { CamelCaseText = true } } Converters = new JsonConverter[] {new StringEnumConverter {CamelCaseText = true}}
}; };
serialized = JsonConvert.SerializeObject(result, settings); serialized = JsonConvert.SerializeObject(result, settings);
} }
if (_jsonCommand != null) if (_jsonCommand != null)
{
serialized = _jsonCommand(context); serialized = _jsonCommand(context);
}
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
await context.Response.WriteAsync(serialized ?? string.Empty); await context.Response.WriteAsync(serialized ?? string.Empty);
......
...@@ -27,7 +27,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -27,7 +27,7 @@ namespace DotNetCore.CAP.Dashboard
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
{ {
ContractResolver = new CamelCasePropertyNamesContractResolver(), ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new JsonConverter[] { new StringEnumConverter { CamelCaseText = true } } Converters = new JsonConverter[] {new StringEnumConverter {CamelCaseText = true}}
}; };
var serialized = JsonConvert.SerializeObject(result, settings); var serialized = JsonConvert.SerializeObject(result, settings);
......
using System; namespace DotNetCore.CAP.Dashboard
namespace DotNetCore.CAP.Dashboard
{ {
public class LocalRequestsOnlyAuthorizationFilter : IDashboardAuthorizationFilter public class LocalRequestsOnlyAuthorizationFilter : IDashboardAuthorizationFilter
{ {
public bool Authorize(DashboardContext context) public bool Authorize(DashboardContext context)
{ {
// if unknown, assume not local // if unknown, assume not local
if (String.IsNullOrEmpty(context.Request.RemoteIpAddress)) if (string.IsNullOrEmpty(context.Request.RemoteIpAddress))
return false; return false;
// check if localhost // check if localhost
......
...@@ -20,12 +20,10 @@ namespace DotNetCore.CAP.Dashboard ...@@ -20,12 +20,10 @@ namespace DotNetCore.CAP.Dashboard
public IEnumerable<DashboardMetric> GetAllMetrics() public IEnumerable<DashboardMetric> GetAllMetrics()
{ {
var metrics = new List<DashboardMetric> { Metric }; var metrics = new List<DashboardMetric> {Metric};
if (Metrics != null) if (Metrics != null)
{
metrics.AddRange(Metrics); metrics.AddRange(Metrics);
}
return metrics.Where(x => x != null).ToList(); return metrics.Where(x => x != null).ToList();
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text; using System.Text;
using DotNetCore.CAP.Infrastructure; using DotNetCore.CAP.Infrastructure;
...@@ -16,7 +18,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -16,7 +18,7 @@ namespace DotNetCore.CAP.Dashboard
private static readonly IDictionary<string, string> ForegroundStateColors private static readonly IDictionary<string, string> ForegroundStateColors
= new Dictionary<string, string>(); = new Dictionary<string, string>();
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
static MessageHistoryRenderer() static MessageHistoryRenderer()
{ {
Register(StatusName.Succeeded, SucceededRenderer); Register(StatusName.Succeeded, SucceededRenderer);
...@@ -44,9 +46,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -44,9 +46,7 @@ namespace DotNetCore.CAP.Dashboard
public static string GetBackgroundStateColor(string stateName) public static string GetBackgroundStateColor(string stateName)
{ {
if (stateName == null || !BackgroundStateColors.ContainsKey(stateName)) if (stateName == null || !BackgroundStateColors.ContainsKey(stateName))
{
return "inherit"; return "inherit";
}
return BackgroundStateColors[stateName]; return BackgroundStateColors[stateName];
} }
...@@ -59,24 +59,19 @@ namespace DotNetCore.CAP.Dashboard ...@@ -59,24 +59,19 @@ namespace DotNetCore.CAP.Dashboard
public static string GetForegroundStateColor(string stateName) public static string GetForegroundStateColor(string stateName)
{ {
if (stateName == null || !ForegroundStateColors.ContainsKey(stateName)) if (stateName == null || !ForegroundStateColors.ContainsKey(stateName))
{
return "inherit"; return "inherit";
}
return ForegroundStateColors[stateName]; return ForegroundStateColors[stateName];
} }
public static void Register(string state, Func<HtmlHelper, IDictionary<string, string>, NonEscapedString> renderer) public static void Register(string state,
Func<HtmlHelper, IDictionary<string, string>, NonEscapedString> renderer)
{ {
if (!Renderers.ContainsKey(state)) if (!Renderers.ContainsKey(state))
{
Renderers.Add(state, renderer); Renderers.Add(state, renderer);
}
else else
{
Renderers[state] = renderer; Renderers[state] = renderer;
} }
}
public static bool Exists(string state) public static bool Exists(string state)
{ {
...@@ -141,10 +136,10 @@ namespace DotNetCore.CAP.Dashboard ...@@ -141,10 +136,10 @@ namespace DotNetCore.CAP.Dashboard
itemsAdded = true; itemsAdded = true;
} }
if (stateData.ContainsKey("Result") && !String.IsNullOrWhiteSpace(stateData["Result"])) if (stateData.ContainsKey("Result") && !string.IsNullOrWhiteSpace(stateData["Result"]))
{ {
var result = stateData["Result"]; var result = stateData["Result"];
builder.Append($"<dt>Result:</dt><dd>{System.Net.WebUtility.HtmlEncode(result)}</dd>"); builder.Append($"<dt>Result:</dt><dd>{WebUtility.HtmlEncode(result)}</dd>");
itemsAdded = true; itemsAdded = true;
} }
...@@ -171,13 +166,9 @@ namespace DotNetCore.CAP.Dashboard ...@@ -171,13 +166,9 @@ namespace DotNetCore.CAP.Dashboard
string serverId = null; string serverId = null;
if (stateData.ContainsKey("ServerId")) if (stateData.ContainsKey("ServerId"))
{
serverId = stateData["ServerId"]; serverId = stateData["ServerId"];
}
else if (stateData.ContainsKey("ServerName")) else if (stateData.ContainsKey("ServerName"))
{
serverId = stateData["ServerName"]; serverId = stateData["ServerName"];
}
if (serverId != null) if (serverId != null)
{ {
......
...@@ -20,7 +20,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -20,7 +20,8 @@ namespace DotNetCore.CAP.Dashboard
Metric = DashboardMetrics.PublishedSucceededCount Metric = DashboardMetrics.PublishedSucceededCount
}); });
PublishedItems.Add(page => new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/published/processing")) PublishedItems.Add(page =>
new MenuItem(Strings.SidebarMenu_Processing, page.Url.To("/published/processing"))
{ {
Active = page.RequestPath.StartsWith("/published/processing"), Active = page.RequestPath.StartsWith("/published/processing"),
Metric = DashboardMetrics.PublishedProcessingCount Metric = DashboardMetrics.PublishedProcessingCount
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
Info, Info,
Success, Success,
Warning, Warning,
Danger, Danger
} }
internal static class MetricStyleExtensions internal static class MetricStyleExtensions
......
...@@ -7,9 +7,9 @@ namespace DotNetCore.CAP.Dashboard ...@@ -7,9 +7,9 @@ namespace DotNetCore.CAP.Dashboard
{ {
private const int PageItemsCount = 7; private const int PageItemsCount = 7;
private const int DefaultRecordsPerPage = 10; private const int DefaultRecordsPerPage = 10;
private int _endPageIndex = 1;
private int _startPageIndex = 1; private int _startPageIndex = 1;
private int _endPageIndex = 1;
public Pager(int from, int perPage, long total) public Pager(int from, int perPage, long total)
{ {
...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.Dashboard
RecordsPerPage = perPage > 0 ? perPage : DefaultRecordsPerPage; RecordsPerPage = perPage > 0 ? perPage : DefaultRecordsPerPage;
TotalRecordCount = total; TotalRecordCount = total;
CurrentPage = FromRecord / RecordsPerPage + 1; CurrentPage = FromRecord / RecordsPerPage + 1;
TotalPageCount = (int)Math.Ceiling((double)TotalRecordCount / RecordsPerPage); TotalPageCount = (int) Math.Ceiling((double) TotalRecordCount / RecordsPerPage);
PagerItems = GenerateItems(); PagerItems = GenerateItems();
} }
...@@ -110,7 +110,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -110,7 +110,7 @@ namespace DotNetCore.CAP.Dashboard
if (_endPageIndex < TotalPageCount - 1) if (_endPageIndex < TotalPageCount - 1)
{ {
var index = _startPageIndex + PageItemsCount; var index = _startPageIndex + PageItemsCount;
if (index > TotalPageCount) { index = TotalPageCount; } if (index > TotalPageCount) index = TotalPageCount;
var item = new Item(index, false, ItemType.MorePage); var item = new Item(index, false, ItemType.MorePage);
results.Add(item); results.Add(item);
} }
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using System
@using System.Collections.Generic
@using DotNetCore.CAP.Models;
@using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages @using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@using DotNetCore.CAP.Models
@using Newtonsoft.Json @using Newtonsoft.Json
@inherits RazorPage @inherits DotNetCore.CAP.Dashboard.RazorPage
@{ @{
Layout = new LayoutPage(Strings.HomePage_Title); Layout = new LayoutPage(Strings.HomePage_Title);
var monitor = Storage.GetMonitoringApi(); var monitor = Storage.GetMonitoringApi();
IDictionary<DateTime, int> publishedSucceeded = monitor.HourlySucceededJobs(MessageType.Publish); var publishedSucceeded = monitor.HourlySucceededJobs(MessageType.Publish);
IDictionary<DateTime, int> publishedFailed = monitor.HourlyFailedJobs(MessageType.Publish); var publishedFailed = monitor.HourlyFailedJobs(MessageType.Publish);
IDictionary<DateTime, int> receivedSucceeded = monitor.HourlySucceededJobs(MessageType.Subscribe); var receivedSucceeded = monitor.HourlySucceededJobs(MessageType.Subscribe);
IDictionary<DateTime, int> receivedFailed = monitor.HourlyFailedJobs(MessageType.Subscribe); var receivedFailed = monitor.HourlyFailedJobs(MessageType.Subscribe);
} }
<div class="row"> <div class="row">
...@@ -41,7 +38,8 @@ ...@@ -41,7 +38,8 @@
data-received-succeeded="@Statistics.ReceivedSucceeded" data-received-succeeded="@Statistics.ReceivedSucceeded"
data-received-failed="@Statistics.ReceivedFailed" data-received-failed="@Statistics.ReceivedFailed"
data-received-succeeded-string="@Strings.HomePage_GraphHover_RSucceeded" data-received-succeeded-string="@Strings.HomePage_GraphHover_RSucceeded"
data-received-failed-string="@Strings.HomePage_GraphHover_RFailed"></div> data-received-failed-string="@Strings.HomePage_GraphHover_RFailed">
</div>
<div style="display: none;"> <div style="display: none;">
<span data-metric="published_succeeded:count"></span> <span data-metric="published_succeeded:count"></span>
<span data-metric="published_failed:count"></span> <span data-metric="published_failed:count"></span>
......
...@@ -2,10 +2,9 @@ ...@@ -2,10 +2,9 @@
@using System @using System
@using System.Globalization @using System.Globalization
@using System.Reflection @using System.Reflection
@using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages @using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@inherits RazorPage @inherits DotNetCore.CAP.Dashboard.RazorPage
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName"> <html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName">
<head> <head>
...@@ -16,9 +15,9 @@ ...@@ -16,9 +15,9 @@
@{ var version = GetType().GetTypeInfo().Assembly.GetName().Version; } @{ var version = GetType().GetTypeInfo().Assembly.GetName().Version; }
<link rel="stylesheet" href="@Url.To($"/css{version.Major}{version.Minor}{version.Build}")"> <link rel="stylesheet" href="@Url.To($"/css{version.Major}{version.Minor}{version.Build}")">
</head> </head>
<body> <body>
<!-- Wrap all page content here --> <!-- Wrap all page content here -->
<div id="wrap"> <div id="wrap">
<!-- Fixed navbar --> <!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top"> <div class="navbar navbar-default navbar-fixed-top">
...@@ -33,7 +32,8 @@ ...@@ -33,7 +32,8 @@
</div> </div>
<div class="collapse navbar-collapse"> <div class="collapse navbar-collapse">
@Html.RenderPartial(new Navigation()) @Html.RenderPartial(new Navigation())
@if(@AppPath != null) { @if (AppPath != null)
{
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li> <li>
<a href="@AppPath"> <a href="@AppPath">
...@@ -52,27 +52,28 @@ ...@@ -52,27 +52,28 @@
<div class="container" style="margin-bottom: 20px;"> <div class="container" style="margin-bottom: 20px;">
@RenderBody() @RenderBody()
</div> </div>
</div> </div>
<div id="footer"> <div id="footer">
<div class="container"> <div class="container">
<ul class="list-inline credit"> <ul class="list-inline credit">
<li> <li>
<a href="https://github.com/dotnetcore/cap/" target="_blank">CAP @($"{version.Major}.{version.Minor}.{version.Build}") <a href="https://github.com/dotnetcore/cap/" target="_blank">
CAP @($"{version.Major}.{version.Minor}.{version.Build}")
</a> </a>
</li> </li>
<li>@Storage</li> <li>@Storage</li>
<li>@Strings.LayoutPage_Footer_Time @Html.LocalTime(DateTime.UtcNow)</li> <li>@Strings.LayoutPage_Footer_Time @Html.LocalTime(DateTime.UtcNow)</li>
<li>@String.Format(Strings.LayoutPage_Footer_Generatedms, GenerationTime.Elapsed.TotalMilliseconds.ToString("N"))</li> <li>@string.Format(Strings.LayoutPage_Footer_Generatedms, GenerationTime.Elapsed.TotalMilliseconds.ToString("N"))</li>
</ul> </ul>
</div> </div>
</div> </div>
<div id="capConfig" <div id="capConfig"
data-pollinterval="@StatsPollingInterval" data-pollinterval="@StatsPollingInterval"
data-pollurl="@(Url.To("/stats"))"> data-pollurl="@(Url.To("/stats"))">
</div> </div>
<script src="@Url.To($"/js{version.Major}{version.Minor}{version.Build}")"></script> <script src="@Url.To($"/js{version.Major}{version.Minor}{version.Build}")"></script>
</body> </body>
</html> </html>
\ No newline at end of file
...@@ -6,12 +6,11 @@ namespace DotNetCore.CAP.Dashboard.Pages ...@@ -6,12 +6,11 @@ namespace DotNetCore.CAP.Dashboard.Pages
{ {
internal partial class NodePage internal partial class NodePage
{ {
private IList<Node> _nodes;
private INodeDiscoveryProvider _discoveryProvider; private INodeDiscoveryProvider _discoveryProvider;
private IList<Node> _nodes;
public NodePage() public NodePage()
{ {
} }
public NodePage(string id) public NodePage(string id)
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages @using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@inherits RazorPage @inherits DotNetCore.CAP.Dashboard.RazorPage
@{ @{
Layout = new LayoutPage(Strings.NodePage_Title); Layout = new LayoutPage(Strings.NodePage_Title);
} }
...@@ -33,7 +32,7 @@ ...@@ -33,7 +32,7 @@
<tbody> <tbody>
@foreach (var node in Nodes) @foreach (var node in Nodes)
{ {
<tr class="@(CurrentNodeId == node.Id? "active":null )"> <tr class="@(CurrentNodeId == node.Id ? "active" : null)">
<td>@node.Id</td> <td>@node.Id</td>
<td>@node.Name</td> <td>@node.Name</td>
<td>@node.Address</td> <td>@node.Address</td>
......
...@@ -13,14 +13,12 @@ namespace DotNetCore.CAP.Dashboard.Pages ...@@ -13,14 +13,12 @@ namespace DotNetCore.CAP.Dashboard.Pages
public int GetTotal(IMonitoringApi api) public int GetTotal(IMonitoringApi api)
{ {
if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded,
{ StringComparison.CurrentCultureIgnoreCase))
return api.PublishedSucceededCount(); return api.PublishedSucceededCount();
} if (string.Equals(StatusName, Infrastructure.StatusName.Processing,
if (string.Equals(StatusName, Infrastructure.StatusName.Processing, StringComparison.CurrentCultureIgnoreCase)) StringComparison.CurrentCultureIgnoreCase))
{
return api.PublishedProcessingCount(); return api.PublishedProcessingCount();
}
return api.PublishedFailedCount(); return api.PublishedFailedCount();
} }
} }
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using System @using System
@using DotNetCore.CAP.Models;
@using DotNetCore.CAP.Dashboard @using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Monitoring @using DotNetCore.CAP.Dashboard.Monitoring
@using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@inherits RazorPage @using DotNetCore.CAP.Models
@inherits DotNetCore.CAP.Dashboard.RazorPage
@{ @{
Layout = new LayoutPage(Strings.PublishedMessagesPage_Title); Layout = new LayoutPage(Strings.PublishedMessagesPage_Title);
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
int.TryParse(Query("from"), out from); int.TryParse(Query("from"), out from);
int.TryParse(Query("count"), out perPage); int.TryParse(Query("count"), out perPage);
string name = Query("name"); var name = Query("name");
string content = Query("content"); var content = Query("content");
var monitor = Storage.GetMonitoringApi(); var monitor = Storage.GetMonitoringApi();
var pager = new Pager(from, perPage, GetTotal(monitor)); var pager = new Pager(from, perPage, GetTotal(monitor));
...@@ -49,11 +49,11 @@ ...@@ -49,11 +49,11 @@
<div class="btn-toolbar btn-toolbar-top"> <div class="btn-toolbar btn-toolbar-top">
<form class="row"> <form class="row">
<span class="col-md-3"> <span class="col-md-3">
<input type="text" class="form-control" name="name" value="@Query("name")" placeholder="@Strings.MessagesPage_Query_MessageName" /> <input type="text" class="form-control" name="name" value="@Query("name")" placeholder="@Strings.MessagesPage_Query_MessageName"/>
</span> </span>
<div class="col-md-5"> <div class="col-md-5">
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" name="content" value="@Query("content")" placeholder="@Strings.MessagesPage_Query_MessageBody" /> <input type="text" class="form-control" name="content" value="@Query("content")" placeholder="@Strings.MessagesPage_Query_MessageBody"/>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-info">@Strings.MessagesPage_Query_Button</button> <button class="btn btn-info">@Strings.MessagesPage_Query_Button</button>
</span> </span>
...@@ -77,8 +77,8 @@ ...@@ -77,8 +77,8 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:60px;"> <th style="width: 60px;">
<input type="checkbox" class="js-jobs-list-select-all" /> <input type="checkbox" class="js-jobs-list-select-all"/>
</th> </th>
<th>@Strings.MessagesPage_Table_Code</th> <th>@Strings.MessagesPage_Table_Code</th>
<th>@Strings.MessagesPage_Table_Name</th> <th>@Strings.MessagesPage_Table_Name</th>
...@@ -95,10 +95,10 @@ ...@@ -95,10 +95,10 @@
{ {
<tr class="js-jobs-list-row hover"> <tr class="js-jobs-list-row hover">
<td> <td>
<input type="checkbox" class="js-jobs-list-checkbox" name="messages[]" value="@message.Id" /> <input type="checkbox" class="js-jobs-list-checkbox" name="messages[]" value="@message.Id"/>
</td> </td>
<td class="word-break"> <td class="word-break">
<a href="javascript:;" data-url='@(Url.To("/published/message/")+message.Id)' class="openModal">#@message.Id</a> <a href="javascript:;" data-url='@(Url.To("/published/message/") + message.Id)' class="openModal">#@message.Id</a>
</td> </td>
<td> <td>
@message.Name @message.Name
...@@ -132,10 +132,12 @@ ...@@ -132,10 +132,12 @@
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Message Content</h4> <h4 class="modal-title">Message Content</h4>
</div> </div>
<div id="jsonContent" style="max-height:500px;overflow-y:auto;" class="modal-body"> <div id="jsonContent" style="max-height: 500px; overflow-y: auto;" class="modal-body">
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-sm btn-primary" id="formatBtn" onclick="">@Strings.MessagesPage_Modal_Format</button> <button type="button" class="btn btn-sm btn-primary" id="formatBtn" onclick="">@Strings.MessagesPage_Modal_Format</button>
......
...@@ -13,14 +13,12 @@ namespace DotNetCore.CAP.Dashboard.Pages ...@@ -13,14 +13,12 @@ namespace DotNetCore.CAP.Dashboard.Pages
public int GetTotal(IMonitoringApi api) public int GetTotal(IMonitoringApi api)
{ {
if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded, StringComparison.CurrentCultureIgnoreCase)) if (string.Equals(StatusName, Infrastructure.StatusName.Succeeded,
{ StringComparison.CurrentCultureIgnoreCase))
return api.ReceivedSucceededCount(); return api.ReceivedSucceededCount();
} if (string.Equals(StatusName, Infrastructure.StatusName.Processing,
if (string.Equals(StatusName, Infrastructure.StatusName.Processing, StringComparison.CurrentCultureIgnoreCase)) StringComparison.CurrentCultureIgnoreCase))
{
return api.ReceivedProcessingCount(); return api.ReceivedProcessingCount();
}
return api.ReceivedFailedCount(); return api.ReceivedFailedCount();
} }
} }
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using System @using System
@using DotNetCore.CAP.Models;
@using DotNetCore.CAP.Dashboard @using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Monitoring @using DotNetCore.CAP.Dashboard.Monitoring
@using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@inherits RazorPage @using DotNetCore.CAP.Models
@inherits DotNetCore.CAP.Dashboard.RazorPage
@{ @{
Layout = new LayoutPage(Strings.ReceivedMessagesPage_Title); Layout = new LayoutPage(Strings.ReceivedMessagesPage_Title);
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
int.TryParse(Query("from"), out from); int.TryParse(Query("from"), out from);
int.TryParse(Query("count"), out perPage); int.TryParse(Query("count"), out perPage);
string group = Query("group"); var group = Query("group");
string name = Query("name"); var name = Query("name");
string content = Query("content"); var content = Query("content");
var monitor = Storage.GetMonitoringApi(); var monitor = Storage.GetMonitoringApi();
var pager = new Pager(from, perPage, GetTotal(monitor)); var pager = new Pager(from, perPage, GetTotal(monitor));
...@@ -51,14 +51,14 @@ ...@@ -51,14 +51,14 @@
<div class="btn-toolbar btn-toolbar-top"> <div class="btn-toolbar btn-toolbar-top">
<form class="row"> <form class="row">
<span class="col-md-2"> <span class="col-md-2">
<input type="text" class="form-control" name="group" value="@Query("group")" placeholder="@Strings.MessagesPage_Query_MessageGroup" /> <input type="text" class="form-control" name="group" value="@Query("group")" placeholder="@Strings.MessagesPage_Query_MessageGroup"/>
</span> </span>
<span class="col-md-3"> <span class="col-md-3">
<input type="text" class="form-control" name="name" value="@Query("name")" placeholder="@Strings.MessagesPage_Query_MessageName" /> <input type="text" class="form-control" name="name" value="@Query("name")" placeholder="@Strings.MessagesPage_Query_MessageName"/>
</span> </span>
<div class="col-md-5"> <div class="col-md-5">
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" name="content" value="@Query("content")" placeholder="@Strings.MessagesPage_Query_MessageBody" /> <input type="text" class="form-control" name="content" value="@Query("content")" placeholder="@Strings.MessagesPage_Query_MessageBody"/>
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-info">@Strings.MessagesPage_Query_Button</button> <button class="btn btn-info">@Strings.MessagesPage_Query_Button</button>
</span> </span>
...@@ -82,8 +82,8 @@ ...@@ -82,8 +82,8 @@
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th style="width:60px;"> <th style="width: 60px;">
<input type="checkbox" class="js-jobs-list-select-all" /> <input type="checkbox" class="js-jobs-list-select-all"/>
</th> </th>
<th>@Strings.MessagesPage_Table_Code</th> <th>@Strings.MessagesPage_Table_Code</th>
<th>@Strings.MessagesPage_Table_Group</th> <th>@Strings.MessagesPage_Table_Group</th>
...@@ -101,10 +101,10 @@ ...@@ -101,10 +101,10 @@
{ {
<tr class="js-jobs-list-row hover"> <tr class="js-jobs-list-row hover">
<td> <td>
<input type="checkbox" class="js-jobs-list-checkbox" name="messages[]" value="@message.Id" /> <input type="checkbox" class="js-jobs-list-checkbox" name="messages[]" value="@message.Id"/>
</td> </td>
<td class="word-break"> <td class="word-break">
<a href="javascript:;" data-url='@(Url.To("/received/message/")+message.Id)' class="openModal">#@message.Id</a> <a href="javascript:;" data-url='@(Url.To("/received/message/") + message.Id)' class="openModal">#@message.Id</a>
</td> </td>
<td> <td>
@message.Group @message.Group
...@@ -141,10 +141,12 @@ ...@@ -141,10 +141,12 @@
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Message Content</h4> <h4 class="modal-title">Message Content</h4>
</div> </div>
<div id="jsonContent" style="max-height:500px;overflow-y:auto;" class="modal-body"> <div id="jsonContent" style="max-height: 500px; overflow-y: auto;" class="modal-body">
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-sm btn-primary" id="formatBtn" onclick="">@Strings.MessagesPage_Modal_Format</button> <button type="button" class="btn btn-sm btn-primary" id="formatBtn" onclick="">@Strings.MessagesPage_Modal_Format</button>
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True *@
@using DotNetCore.CAP.Internal
@using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Pages @using DotNetCore.CAP.Dashboard.Pages
@using DotNetCore.CAP.Dashboard.Resources @using DotNetCore.CAP.Dashboard.Resources
@inherits RazorPage @using DotNetCore.CAP.Internal
@inherits DotNetCore.CAP.Dashboard.RazorPage
@{ @{
Layout = new LayoutPage(Strings.SubscribersPage_Title); Layout = new LayoutPage(Strings.SubscribersPage_Title);
...@@ -48,7 +47,7 @@ ...@@ -48,7 +47,7 @@
} }
<td>@column.Attribute.Name</td> <td>@column.Attribute.Name</td>
<td> <td>
<span style="color:#00bcd4">@column.ImplTypeInfo.Name</span>: <span style="color: #00bcd4">@column.ImplTypeInfo.Name</span>:
<div class="job-snippet-code"> <div class="job-snippet-code">
<code> <code>
<pre>@Html.MethodEscaped(column.MethodInfo)</pre> <pre>@Html.MethodEscaped(column.MethodInfo)</pre>
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@
@using DotNetCore.CAP.Dashboard @inherits DotNetCore.CAP.Dashboard.RazorPage
@inherits RazorPage
<ol class="breadcrumb"> <ol class="breadcrumb">
<li><a href="@Url.Home()"><span class="glyphicon glyphicon-home"></span></a></li> <li>
<a href="@Url.Home()">
<span class="glyphicon glyphicon-home"></span>
</a>
</li>
@foreach (var item in Items) @foreach (var item in Items)
{ {
<li><a href="@item.Value">@item.Key</a></li> <li>
<a href="@item.Value">@item.Key</a>
</li>
} }
<li class="active">@Title</li> <li class="active">@Title</li>
</ol> </ol>
\ No newline at end of file
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@
@using DotNetCore.CAP.Dashboard @using DotNetCore.CAP.Dashboard
@inherits RazorPage @inherits DotNetCore.CAP.Dashboard.RazorPage
@if (NavigationMenu.Items.Count > 0) @if (NavigationMenu.Items.Count > 0)
{ {
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
...@@ -8,7 +8,10 @@ ...@@ -8,7 +8,10 @@
{ {
var itemValue = item(this); var itemValue = item(this);
if (itemValue == null) { continue; } if (itemValue == null)
{
continue;
}
<li class="@(itemValue.Active ? "active" : null)"> <li class="@(itemValue.Active ? "active" : null)">
<a href="@itemValue.Url"> <a href="@itemValue.Url">
......
namespace DotNetCore.CAP.Dashboard.Pages namespace DotNetCore.CAP.Dashboard.Pages
{ {
partial class Paginator internal partial class Paginator
{ {
private readonly Pager _pager; private readonly Pager _pager;
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@
@using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Resources;
@inherits RazorPage @using DotNetCore.CAP.Dashboard
@using DotNetCore.CAP.Dashboard.Resources
@inherits DotNetCore.CAP.Dashboard.RazorPage
<div class="btn-toolbar"> <div class="btn-toolbar">
@if (_pager.TotalPageCount > 1) @if (_pager.TotalPageCount > 1)
{ {
......
namespace DotNetCore.CAP.Dashboard.Pages namespace DotNetCore.CAP.Dashboard.Pages
{ {
partial class PerPageSelector internal partial class PerPageSelector
{ {
private readonly Pager _pager; private readonly Pager _pager;
......
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@
@using DotNetCore.CAP.Dashboard.Resources; @using DotNetCore.CAP.Dashboard.Resources
@inherits DotNetCore.CAP.Dashboard.RazorPage @inherits DotNetCore.CAP.Dashboard.RazorPage
<div class="btn-group pull-right paginator"> <div class="btn-group pull-right paginator">
@foreach (var count in new[] { 10, 20, 50, 100, 500 }) @foreach (var count in new[] {10, 20, 50, 100, 500})
{ {
<a class="btn btn-sm btn-default @(count == _pager.RecordsPerPage ? "active" : null)" <a class="btn btn-sm btn-default @(count == _pager.RecordsPerPage ? "active" : null)"
href="@_pager.RecordsPerPageUrl(count)">@count</a> href="@_pager.RecordsPerPageUrl(count)">
@count</a>
} }
</div> </div>
<div class="btn-toolbar-spacer pull-right"></div> <div class="btn-toolbar-spacer pull-right"></div>
<div class="btn-toolbar-label btn-toolbar-label-sm pull-right"> <div class="btn-toolbar-label btn-toolbar-label-sm pull-right">
@Strings.PerPageSelector_ItemsPerPage: @Strings.PerPageSelector_ItemsPerPage:
</div> </div>
\ No newline at end of file
@* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@ @* Generator: Template TypeVisibility: Internal GeneratePrettyNames: True TrimLeadingUnderscores : true *@
@using DotNetCore.CAP.Dashboard @inherits DotNetCore.CAP.Dashboard.RazorPage
@inherits RazorPage
@if (Items.Any()) @if (Items.Any())
{ {
<div id="stats" class="list-group"> <div id="stats" class="list-group">
......
...@@ -8,10 +8,9 @@ namespace DotNetCore.CAP.Dashboard ...@@ -8,10 +8,9 @@ namespace DotNetCore.CAP.Dashboard
{ {
public abstract class RazorPage public abstract class RazorPage
{ {
private Lazy<StatisticsDto> _statisticsLazy;
private readonly StringBuilder _content = new StringBuilder(); private readonly StringBuilder _content = new StringBuilder();
private string _body; private string _body;
private Lazy<StatisticsDto> _statisticsLazy;
protected RazorPage() protected RazorPage()
{ {
...@@ -20,7 +19,7 @@ namespace DotNetCore.CAP.Dashboard ...@@ -20,7 +19,7 @@ namespace DotNetCore.CAP.Dashboard
} }
public RazorPage Layout { get; protected set; } public RazorPage Layout { get; protected set; }
public HtmlHelper Html { get; private set; } public HtmlHelper Html { get; }
public UrlHelper Url { get; private set; } public UrlHelper Url { get; private set; }
public IStorage Storage { get; internal set; } public IStorage Storage { get; internal set; }
...@@ -86,10 +85,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -86,10 +85,8 @@ namespace DotNetCore.CAP.Dashboard
var monitoring = Storage.GetMonitoringApi(); var monitoring = Storage.GetMonitoringApi();
var dto = monitoring.GetStatistics(); var dto = monitoring.GetStatistics();
if (CapCache.Global.TryGet("cap.nodes.count", out object count)) if (CapCache.Global.TryGet("cap.nodes.count", out var count))
{ dto.Servers = (int) count;
dto.Servers = (int)count;
}
return dto; return dto;
}); });
......
...@@ -36,10 +36,8 @@ namespace DotNetCore.CAP.Dashboard ...@@ -36,10 +36,8 @@ namespace DotNetCore.CAP.Dashboard
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (match.Success) if (match.Success)
{
return new Tuple<IDashboardDispatcher, Match>(dispatcher.Item2, match); return new Tuple<IDashboardDispatcher, Match>(dispatcher.Item2, match);
} }
}
return null; return null;
} }
......
...@@ -9,14 +9,14 @@ using Microsoft.Extensions.Options; ...@@ -9,14 +9,14 @@ using Microsoft.Extensions.Options;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
/// <summary> /// <summary>
/// Default implement of <see cref="IBootstrapper"/>. /// Default implement of <see cref="IBootstrapper" />.
/// </summary> /// </summary>
internal class DefaultBootstrapper : IBootstrapper internal class DefaultBootstrapper : IBootstrapper
{ {
private readonly ILogger<DefaultBootstrapper> _logger;
private readonly IApplicationLifetime _appLifetime; private readonly IApplicationLifetime _appLifetime;
private readonly CancellationTokenSource _cts; private readonly CancellationTokenSource _cts;
private readonly CancellationTokenRegistration _ctsRegistration; private readonly CancellationTokenRegistration _ctsRegistration;
private readonly ILogger<DefaultBootstrapper> _logger;
private Task _bootstrappingTask; private Task _bootstrappingTask;
public DefaultBootstrapper( public DefaultBootstrapper(
...@@ -55,7 +55,7 @@ namespace DotNetCore.CAP ...@@ -55,7 +55,7 @@ namespace DotNetCore.CAP
public Task BootstrapAsync() public Task BootstrapAsync()
{ {
return (_bootstrappingTask = BootstrapTaskAsync()); return _bootstrappingTask = BootstrapTaskAsync();
} }
private async Task BootstrapTaskAsync() private async Task BootstrapTaskAsync()
...@@ -69,7 +69,6 @@ namespace DotNetCore.CAP ...@@ -69,7 +69,6 @@ namespace DotNetCore.CAP
if (_cts.IsCancellationRequested) return; if (_cts.IsCancellationRequested) return;
foreach (var item in Servers) foreach (var item in Servers)
{
try try
{ {
item.Start(); item.Start();
...@@ -78,7 +77,6 @@ namespace DotNetCore.CAP ...@@ -78,7 +77,6 @@ namespace DotNetCore.CAP
{ {
_logger.ServerStartedError(ex); _logger.ServerStartedError(ex);
} }
}
_ctsRegistration.Dispose(); _ctsRegistration.Dispose();
_cts.Dispose(); _cts.Dispose();
...@@ -89,9 +87,7 @@ namespace DotNetCore.CAP ...@@ -89,9 +87,7 @@ namespace DotNetCore.CAP
_appLifetime.ApplicationStopping.Register(() => _appLifetime.ApplicationStopping.Register(() =>
{ {
foreach (var item in Servers) foreach (var item in Servers)
{
item.Dispose(); item.Dispose();
}
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }
......
...@@ -40,9 +40,10 @@ namespace DotNetCore.CAP ...@@ -40,9 +40,10 @@ namespace DotNetCore.CAP
/// <param name="name">the topic name or exchange router key.</param> /// <param name="name">the topic name or exchange router key.</param>
/// <param name="contentObj">message body content, that will be serialized of json.</param> /// <param name="contentObj">message body content, that will be serialized of json.</param>
/// <param name="callbackName">callback subscriber name</param> /// <param name="callbackName">callback subscriber name</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param> /// <param name="dbConnection">the connection of <see cref="IDbConnection" /></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param> /// <param name="dbTransaction">the transaction of <see cref="IDbTransaction" /></param>
Task PublishAsync<T>(string name, T contentObj, IDbConnection dbConnection, string callbackName = null, IDbTransaction dbTransaction = null); Task PublishAsync<T>(string name, T contentObj, IDbConnection dbConnection, string callbackName = null,
IDbTransaction dbTransaction = null);
/// <summary> /// <summary>
/// (ado.net) Publish a object message. /// (ado.net) Publish a object message.
...@@ -50,8 +51,9 @@ namespace DotNetCore.CAP ...@@ -50,8 +51,9 @@ namespace DotNetCore.CAP
/// <param name="name">the topic name or exchange router key.</param> /// <param name="name">the topic name or exchange router key.</param>
/// <param name="contentObj">message body content, that will be serialized of json.</param> /// <param name="contentObj">message body content, that will be serialized of json.</param>
/// <param name="callbackName">callback subscriber name</param> /// <param name="callbackName">callback subscriber name</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param> /// <param name="dbConnection">the connection of <see cref="IDbConnection" /></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param> /// <param name="dbTransaction">the transaction of <see cref="IDbTransaction" /></param>
void Publish<T>(string name, T contentObj, IDbConnection dbConnection, string callbackName = null, IDbTransaction dbTransaction = null); void Publish<T>(string name, T contentObj, IDbConnection dbConnection, string callbackName = null,
IDbTransaction dbTransaction = null);
} }
} }
\ No newline at end of file
...@@ -15,7 +15,7 @@ namespace DotNetCore.CAP ...@@ -15,7 +15,7 @@ namespace DotNetCore.CAP
void Commit(); void Commit();
event EventHandler<MessageContext> OnMessageReceieved; event EventHandler<MessageContext> OnMessageReceived;
event EventHandler<string> OnError; event EventHandler<string> OnError;
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
public interface IConsumerClientFactory public interface IConsumerClientFactory
{ {
/// <summary> /// <summary>
/// Create a new instance of <see cref="IConsumerClient"/>. /// Create a new instance of <see cref="IConsumerClient" />.
/// </summary> /// </summary>
/// <param name="groupId"></param> /// <param name="groupId"></param>
IConsumerClient Create(string groupId); IConsumerClient Create(string groupId);
......
...@@ -13,14 +13,14 @@ namespace DotNetCore.CAP ...@@ -13,14 +13,14 @@ namespace DotNetCore.CAP
{ {
internal class ConsumerHandler : IConsumerHandler internal class ConsumerHandler : IConsumerHandler
{ {
private readonly IServiceProvider _serviceProvider;
private readonly IConsumerClientFactory _consumerClientFactory; private readonly IConsumerClientFactory _consumerClientFactory;
private readonly ILogger _logger;
private readonly CancellationTokenSource _cts; private readonly CancellationTokenSource _cts;
private readonly MethodMatcherCache _selector; private readonly ILogger _logger;
private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1); private readonly TimeSpan _pollingDelay = TimeSpan.FromSeconds(1);
private readonly MethodMatcherCache _selector;
private readonly IServiceProvider _serviceProvider;
private Task _compositeTask; private Task _compositeTask;
private bool _disposed; private bool _disposed;
...@@ -43,7 +43,6 @@ namespace DotNetCore.CAP ...@@ -43,7 +43,6 @@ namespace DotNetCore.CAP
var groupingMatchs = _selector.GetCandidatesMethodsOfGroupNameGrouped(); var groupingMatchs = _selector.GetCandidatesMethodsOfGroupNameGrouped();
foreach (var matchGroup in groupingMatchs) foreach (var matchGroup in groupingMatchs)
{
Task.Factory.StartNew(() => Task.Factory.StartNew(() =>
{ {
using (var client = _consumerClientFactory.Create(matchGroup.Key)) using (var client = _consumerClientFactory.Create(matchGroup.Key))
...@@ -55,16 +54,13 @@ namespace DotNetCore.CAP ...@@ -55,16 +54,13 @@ namespace DotNetCore.CAP
client.Listening(_pollingDelay, _cts.Token); client.Listening(_pollingDelay, _cts.Token);
} }
}, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }, _cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
_compositeTask = Task.CompletedTask; _compositeTask = Task.CompletedTask;
} }
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
{
return; return;
}
_disposed = true; _disposed = true;
_logger.ServerShuttingDown(); _logger.ServerShuttingDown();
...@@ -78,15 +74,18 @@ namespace DotNetCore.CAP ...@@ -78,15 +74,18 @@ namespace DotNetCore.CAP
{ {
var innerEx = ex.InnerExceptions[0]; var innerEx = ex.InnerExceptions[0];
if (!(innerEx is OperationCanceledException)) if (!(innerEx is OperationCanceledException))
{
_logger.ExpectedOperationCanceledException(innerEx); _logger.ExpectedOperationCanceledException(innerEx);
} }
} }
public void Pulse()
{
SubscribeQueuer.PulseEvent.Set();
} }
private void RegisterMessageProcessor(IConsumerClient client) private void RegisterMessageProcessor(IConsumerClient client)
{ {
client.OnMessageReceieved += (sender, message) => client.OnMessageReceived += (sender, message) =>
{ {
_logger.EnqueuingReceivedMessage(message.Name, message.Content); _logger.EnqueuingReceivedMessage(message.Name, message.Content);
...@@ -99,10 +98,7 @@ namespace DotNetCore.CAP ...@@ -99,10 +98,7 @@ namespace DotNetCore.CAP
Pulse(); Pulse();
}; };
client.OnError += (sender, reason) => client.OnError += (sender, reason) => { _logger.LogError(reason); };
{
_logger.LogError(reason);
};
} }
private static void StoreMessage(IServiceScope serviceScope, MessageContext messageContext) private static void StoreMessage(IServiceScope serviceScope, MessageContext messageContext)
...@@ -111,14 +107,9 @@ namespace DotNetCore.CAP ...@@ -111,14 +107,9 @@ namespace DotNetCore.CAP
var messageStore = provider.GetRequiredService<IStorageConnection>(); var messageStore = provider.GetRequiredService<IStorageConnection>();
var receivedMessage = new CapReceivedMessage(messageContext) var receivedMessage = new CapReceivedMessage(messageContext)
{ {
StatusName = StatusName.Scheduled, StatusName = StatusName.Scheduled
}; };
messageStore.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult(); messageStore.StoreReceivedMessageAsync(receivedMessage).GetAwaiter().GetResult();
} }
public void Pulse()
{
SubscribeQueuer.PulseEvent.Set();
}
} }
} }
\ No newline at end of file
...@@ -10,9 +10,9 @@ namespace DotNetCore.CAP ...@@ -10,9 +10,9 @@ namespace DotNetCore.CAP
{ {
public abstract class BasePublishQueueExecutor : IQueueExecutor public abstract class BasePublishQueueExecutor : IQueueExecutor
{ {
private readonly ILogger _logger;
private readonly CapOptions _options; private readonly CapOptions _options;
private readonly IStateChanger _stateChanger; private readonly IStateChanger _stateChanger;
private readonly ILogger _logger;
protected BasePublishQueueExecutor( protected BasePublishQueueExecutor(
CapOptions options, CapOptions options,
...@@ -24,8 +24,6 @@ namespace DotNetCore.CAP ...@@ -24,8 +24,6 @@ namespace DotNetCore.CAP
_logger = logger; _logger = logger;
} }
public abstract Task<OperateResult> PublishAsync(string keyName, string content);
public async Task<OperateResult> ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched) public async Task<OperateResult> ExecuteAsync(IStorageConnection connection, IFetchedMessage fetched)
{ {
var message = await connection.GetPublishedMessageAsync(fetched.MessageId); var message = await connection.GetPublishedMessageAsync(fetched.MessageId);
...@@ -35,9 +33,7 @@ namespace DotNetCore.CAP ...@@ -35,9 +33,7 @@ namespace DotNetCore.CAP
await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection); await _stateChanger.ChangeStateAsync(message, new ProcessingState(), connection);
if (message.Retries > 0) if (message.Retries > 0)
{
_logger.JobRetrying(message.Retries); _logger.JobRetrying(message.Retries);
}
var result = await PublishAsync(message.Name, message.Content); var result = await PublishAsync(message.Name, message.Content);
sp.Stop(); sp.Stop();
...@@ -65,9 +61,7 @@ namespace DotNetCore.CAP ...@@ -65,9 +61,7 @@ namespace DotNetCore.CAP
fetched.RemoveFromQueue(); fetched.RemoveFromQueue();
if (result.Succeeded) if (result.Succeeded)
{
_logger.JobExecuted(sp.Elapsed.TotalSeconds); _logger.JobExecuted(sp.Elapsed.TotalSeconds);
}
return OperateResult.Success; return OperateResult.Success;
} }
...@@ -78,15 +72,16 @@ namespace DotNetCore.CAP ...@@ -78,15 +72,16 @@ namespace DotNetCore.CAP
} }
} }
private static async Task<bool> UpdateMessageForRetryAsync(CapPublishedMessage message, IStorageConnection connection) public abstract Task<OperateResult> PublishAsync(string keyName, string content);
private static async Task<bool> UpdateMessageForRetryAsync(CapPublishedMessage message,
IStorageConnection connection)
{ {
var retryBehavior = RetryBehavior.DefaultRetry; var retryBehavior = RetryBehavior.DefaultRetry;
var retries = ++message.Retries; var retries = ++message.Retries;
if (retries >= retryBehavior.RetryCount) if (retries >= retryBehavior.RetryCount)
{
return false; return false;
}
var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries)); var due = message.Added.AddSeconds(retryBehavior.RetryIn(retries));
message.ExpiresAt = due; message.ExpiresAt = due;
......
...@@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; ...@@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class SubscibeQueueExecutor : IQueueExecutor public class SubscribeQueueExecutor : IQueueExecutor
{ {
private readonly IConsumerInvokerFactory _consumerInvokerFactory; private readonly IConsumerInvokerFactory _consumerInvokerFactory;
private readonly ILogger _logger; private readonly ILogger _logger;
...@@ -20,7 +20,7 @@ namespace DotNetCore.CAP ...@@ -20,7 +20,7 @@ namespace DotNetCore.CAP
private readonly MethodMatcherCache _selector; private readonly MethodMatcherCache _selector;
private readonly IStateChanger _stateChanger; private readonly IStateChanger _stateChanger;
public SubscibeQueueExecutor( public SubscribeQueueExecutor(
IStateChanger stateChanger, IStateChanger stateChanger,
MethodMatcherCache selector, MethodMatcherCache selector,
CapOptions options, CapOptions options,
......
...@@ -50,15 +50,15 @@ namespace DotNetCore.CAP ...@@ -50,15 +50,15 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Returns the next message to be enqueued. /// Returns the next message to be enqueued.
/// </summary> /// </summary>
Task<CapReceivedMessage> GetNextReceviedMessageToBeEnqueuedAsync(); Task<CapReceivedMessage> GetNextReceivedMessageToBeEnqueuedAsync();
/// <summary> /// <summary>
/// Returns executed failed message. /// Returns executed failed message.
/// </summary> /// </summary>
Task<IEnumerable<CapReceivedMessage>> GetFailedReceviedMessages(); Task<IEnumerable<CapReceivedMessage>> GetFailedReceivedMessages();
/// <summary> /// <summary>
/// Creates and returns an <see cref="IStorageTransaction"/>. /// Creates and returns an <see cref="IStorageTransaction" />.
/// </summary> /// </summary>
IStorageTransaction CreateTransaction(); IStorageTransaction CreateTransaction();
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
...@@ -44,45 +43,25 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -44,45 +43,25 @@ namespace DotNetCore.CAP.Infrastructure
public static long ToTimestamp(DateTime value) public static long ToTimestamp(DateTime value)
{ {
var elapsedTime = value - Epoch; var elapsedTime = value - Epoch;
return (long)elapsedTime.TotalSeconds; return (long) elapsedTime.TotalSeconds;
} }
public static DateTime FromTimestamp(long value) public static DateTime FromTimestamp(long value)
{ {
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
return Epoch.AddSeconds(value); return Epoch.AddSeconds(value);
} }
public static string SerializeDateTime(DateTime value)
{
return value.ToString("o", CultureInfo.InvariantCulture);
}
public static DateTime DeserializeDateTime(string value)
{
if (long.TryParse(value, out var timestamp))
{
return FromTimestamp(timestamp);
}
return DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
}
public static bool IsController(TypeInfo typeInfo) public static bool IsController(TypeInfo typeInfo)
{ {
if (!typeInfo.IsClass) if (!typeInfo.IsClass)
{
return false; return false;
}
if (typeInfo.IsAbstract) if (typeInfo.IsAbstract)
{
return false; return false;
}
if (!typeInfo.IsPublic) if (!typeInfo.IsPublic)
{
return false; return false;
}
return !typeInfo.ContainsGenericParameters return !typeInfo.ContainsGenericParameters
&& typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase); && typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase);
...@@ -97,9 +76,7 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -97,9 +76,7 @@ namespace DotNetCore.CAP.Infrastructure
{ {
var jObj = JObject.Parse(json); var jObj = JObject.Parse(json);
foreach (var property in properties) foreach (var property in properties)
{
jObj.Add(new JProperty(property.Key, property.Value)); jObj.Add(new JProperty(property.Key, property.Value));
}
return jObj.ToString(); return jObj.ToString();
} }
......
...@@ -19,25 +19,24 @@ namespace DotNetCore.CAP ...@@ -19,25 +19,24 @@ namespace DotNetCore.CAP
private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch; private static readonly long __dateTimeMaxValueMillisecondsSinceEpoch;
private static readonly long __dateTimeMinValueMillisecondsSinceEpoch; private static readonly long __dateTimeMinValueMillisecondsSinceEpoch;
private static ObjectId __emptyInstance = default(ObjectId); private static readonly int __staticMachine;
private static int __staticMachine; private static readonly short __staticPid;
private static short __staticPid;
private static int __staticIncrement; // high byte will be masked out when generating new ObjectId private static int __staticIncrement; // high byte will be masked out when generating new ObjectId
private static uint[] _lookup32 = Enumerable.Range(0, 256).Select(i => private static readonly uint[] _lookup32 = Enumerable.Range(0, 256).Select(i =>
{ {
string s = i.ToString("x2"); var s = i.ToString("x2");
return ((uint)s[0]) + ((uint)s[1] << 16); return (uint) s[0] + ((uint) s[1] << 16);
}).ToArray(); }).ToArray();
// we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap // we're using 14 bytes instead of 12 to hold the ObjectId in memory but unlike a byte[] there is no additional object on the heap
// the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification // the extra two bytes are not visible to anyone outside of this class and they buy us considerable simplification
// an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems // an additional advantage of this representation is that it will serialize to JSON without any 64 bit overflow problems
private int _timestamp; private readonly int _timestamp;
private int _machine; private readonly int _machine;
private short _pid; private readonly short _pid;
private int _increment; private readonly int _increment;
// static constructor // static constructor
static ObjectId() static ObjectId()
...@@ -46,8 +45,8 @@ namespace DotNetCore.CAP ...@@ -46,8 +45,8 @@ namespace DotNetCore.CAP
__dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - __unixEpoch).Ticks / 10000; __dateTimeMaxValueMillisecondsSinceEpoch = (DateTime.MaxValue - __unixEpoch).Ticks / 10000;
__dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - __unixEpoch).Ticks / 10000; __dateTimeMinValueMillisecondsSinceEpoch = (DateTime.MinValue - __unixEpoch).Ticks / 10000;
__staticMachine = GetMachineHash(); __staticMachine = GetMachineHash();
__staticIncrement = (new Random()).Next(); __staticIncrement = new Random().Next();
__staticPid = (short)GetCurrentProcessId(); __staticPid = (short) GetCurrentProcessId();
} }
// constructors // constructors
...@@ -58,9 +57,7 @@ namespace DotNetCore.CAP ...@@ -58,9 +57,7 @@ namespace DotNetCore.CAP
public ObjectId(byte[] bytes) public ObjectId(byte[] bytes)
{ {
if (bytes == null) if (bytes == null)
{
throw new ArgumentNullException("bytes"); throw new ArgumentNullException("bytes");
}
Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment); Unpack(bytes, out _timestamp, out _machine, out _pid, out _increment);
} }
...@@ -86,13 +83,11 @@ namespace DotNetCore.CAP ...@@ -86,13 +83,11 @@ namespace DotNetCore.CAP
public ObjectId(int timestamp, int machine, short pid, int increment) public ObjectId(int timestamp, int machine, short pid, int increment)
{ {
if ((machine & 0xff000000) != 0) if ((machine & 0xff000000) != 0)
{ throw new ArgumentOutOfRangeException("machine",
throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
}
if ((increment & 0xff000000) != 0) if ((increment & 0xff000000) != 0)
{ throw new ArgumentOutOfRangeException("increment",
throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
}
_timestamp = timestamp; _timestamp = timestamp;
_machine = machine; _machine = machine;
...@@ -107,9 +102,7 @@ namespace DotNetCore.CAP ...@@ -107,9 +102,7 @@ namespace DotNetCore.CAP
public ObjectId(string value) public ObjectId(string value)
{ {
if (value == null) if (value == null)
{
throw new ArgumentNullException("value"); throw new ArgumentNullException("value");
}
Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment); Unpack(ParseHexString(value), out _timestamp, out _machine, out _pid, out _increment);
} }
...@@ -117,51 +110,33 @@ namespace DotNetCore.CAP ...@@ -117,51 +110,33 @@ namespace DotNetCore.CAP
/// <summary> /// <summary>
/// Gets an instance of ObjectId where the value is empty. /// Gets an instance of ObjectId where the value is empty.
/// </summary> /// </summary>
public static ObjectId Empty public static ObjectId Empty { get; } = default(ObjectId);
{
get { return __emptyInstance; }
}
// public properties // public properties
/// <summary> /// <summary>
/// Gets the timestamp. /// Gets the timestamp.
/// </summary> /// </summary>
public int Timestamp public int Timestamp => _timestamp;
{
get { return _timestamp; }
}
/// <summary> /// <summary>
/// Gets the machine. /// Gets the machine.
/// </summary> /// </summary>
public int Machine public int Machine => _machine;
{
get { return _machine; }
}
/// <summary> /// <summary>
/// Gets the PID. /// Gets the PID.
/// </summary> /// </summary>
public short Pid public short Pid => _pid;
{
get { return _pid; }
}
/// <summary> /// <summary>
/// Gets the increment. /// Gets the increment.
/// </summary> /// </summary>
public int Increment public int Increment => _increment;
{
get { return _increment; }
}
/// <summary> /// <summary>
/// Gets the creation time (derived from the timestamp). /// Gets the creation time (derived from the timestamp).
/// </summary> /// </summary>
public DateTime CreationTime public DateTime CreationTime => __unixEpoch.AddSeconds(_timestamp);
{
get { return __unixEpoch.AddSeconds(_timestamp); }
}
// public operators // public operators
/// <summary> /// <summary>
...@@ -257,7 +232,7 @@ namespace DotNetCore.CAP ...@@ -257,7 +232,7 @@ namespace DotNetCore.CAP
/// <returns>An ObjectId.</returns> /// <returns>An ObjectId.</returns>
public static ObjectId GenerateNewId(int timestamp) public static ObjectId GenerateNewId(int timestamp)
{ {
int increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes var increment = Interlocked.Increment(ref __staticIncrement) & 0x00ffffff; // only use low order 3 bytes
return new ObjectId(timestamp, __staticMachine, __staticPid, increment); return new ObjectId(timestamp, __staticMachine, __staticPid, increment);
} }
...@@ -281,27 +256,25 @@ namespace DotNetCore.CAP ...@@ -281,27 +256,25 @@ namespace DotNetCore.CAP
public static byte[] Pack(int timestamp, int machine, short pid, int increment) public static byte[] Pack(int timestamp, int machine, short pid, int increment)
{ {
if ((machine & 0xff000000) != 0) if ((machine & 0xff000000) != 0)
{ throw new ArgumentOutOfRangeException("machine",
throw new ArgumentOutOfRangeException("machine", "The machine value must be between 0 and 16777215 (it must fit in 3 bytes)."); "The machine value must be between 0 and 16777215 (it must fit in 3 bytes).");
}
if ((increment & 0xff000000) != 0) if ((increment & 0xff000000) != 0)
{ throw new ArgumentOutOfRangeException("increment",
throw new ArgumentOutOfRangeException("increment", "The increment value must be between 0 and 16777215 (it must fit in 3 bytes)."); "The increment value must be between 0 and 16777215 (it must fit in 3 bytes).");
}
var bytes = new byte[12];
byte[] bytes = new byte[12]; bytes[0] = (byte) (timestamp >> 24);
bytes[0] = (byte)(timestamp >> 24); bytes[1] = (byte) (timestamp >> 16);
bytes[1] = (byte)(timestamp >> 16); bytes[2] = (byte) (timestamp >> 8);
bytes[2] = (byte)(timestamp >> 8); bytes[3] = (byte) timestamp;
bytes[3] = (byte)(timestamp); bytes[4] = (byte) (machine >> 16);
bytes[4] = (byte)(machine >> 16); bytes[5] = (byte) (machine >> 8);
bytes[5] = (byte)(machine >> 8); bytes[6] = (byte) machine;
bytes[6] = (byte)(machine); bytes[7] = (byte) (pid >> 8);
bytes[7] = (byte)(pid >> 8); bytes[8] = (byte) pid;
bytes[8] = (byte)(pid); bytes[9] = (byte) (increment >> 16);
bytes[9] = (byte)(increment >> 16); bytes[10] = (byte) (increment >> 8);
bytes[10] = (byte)(increment >> 8); bytes[11] = (byte) increment;
bytes[11] = (byte)(increment);
return bytes; return bytes;
} }
...@@ -313,13 +286,9 @@ namespace DotNetCore.CAP ...@@ -313,13 +286,9 @@ namespace DotNetCore.CAP
public static ObjectId Parse(string s) public static ObjectId Parse(string s)
{ {
if (s == null) if (s == null)
{
throw new ArgumentNullException("s"); throw new ArgumentNullException("s");
}
if (s.Length != 24) if (s.Length != 24)
{
throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters."); throw new ArgumentOutOfRangeException("s", "ObjectId string value must be 24 characters.");
}
return new ObjectId(ParseHexString(s)); return new ObjectId(ParseHexString(s));
} }
...@@ -334,16 +303,12 @@ namespace DotNetCore.CAP ...@@ -334,16 +303,12 @@ namespace DotNetCore.CAP
public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment) public static void Unpack(byte[] bytes, out int timestamp, out int machine, out short pid, out int increment)
{ {
if (bytes == null) if (bytes == null)
{
throw new ArgumentNullException("bytes"); throw new ArgumentNullException("bytes");
}
if (bytes.Length != 12) if (bytes.Length != 12)
{
throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long."); throw new ArgumentOutOfRangeException("bytes", "Byte array must be 12 bytes long.");
}
timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3]; timestamp = (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6]; machine = (bytes[4] << 16) + (bytes[5] << 8) + bytes[6];
pid = (short)((bytes[7] << 8) + bytes[8]); pid = (short) ((bytes[7] << 8) + bytes[8]);
increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11]; increment = (bytes[9] << 16) + (bytes[10] << 8) + bytes[11];
} }
...@@ -369,7 +334,7 @@ namespace DotNetCore.CAP ...@@ -369,7 +334,7 @@ namespace DotNetCore.CAP
private static int GetTimestampFromDateTime(DateTime timestamp) private static int GetTimestampFromDateTime(DateTime timestamp)
{ {
return (int)Math.Floor((ToUniversalTime(timestamp) - __unixEpoch).TotalSeconds); return (int) Math.Floor((ToUniversalTime(timestamp) - __unixEpoch).TotalSeconds);
} }
// public methods // public methods
...@@ -377,15 +342,18 @@ namespace DotNetCore.CAP ...@@ -377,15 +342,18 @@ namespace DotNetCore.CAP
/// Compares this ObjectId to another ObjectId. /// Compares this ObjectId to another ObjectId.
/// </summary> /// </summary>
/// <param name="other">The other ObjectId.</param> /// <param name="other">The other ObjectId.</param>
/// <returns>A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the other.</returns> /// <returns>
/// A 32-bit signed integer that indicates whether this ObjectId is less than, equal to, or greather than the
/// other.
/// </returns>
public int CompareTo(ObjectId other) public int CompareTo(ObjectId other)
{ {
int r = _timestamp.CompareTo(other._timestamp); var r = _timestamp.CompareTo(other._timestamp);
if (r != 0) { return r; } if (r != 0) return r;
r = _machine.CompareTo(other._machine); r = _machine.CompareTo(other._machine);
if (r != 0) { return r; } if (r != 0) return r;
r = _pid.CompareTo(other._pid); r = _pid.CompareTo(other._pid);
if (r != 0) { return r; } if (r != 0) return r;
return _increment.CompareTo(other._increment); return _increment.CompareTo(other._increment);
} }
...@@ -411,14 +379,9 @@ namespace DotNetCore.CAP ...@@ -411,14 +379,9 @@ namespace DotNetCore.CAP
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
if (obj is ObjectId) if (obj is ObjectId)
{ return Equals((ObjectId) obj);
return Equals((ObjectId)obj);
}
else
{
return false; return false;
} }
}
/// <summary> /// <summary>
/// Gets the hash code. /// Gets the hash code.
...@@ -426,7 +389,7 @@ namespace DotNetCore.CAP ...@@ -426,7 +389,7 @@ namespace DotNetCore.CAP
/// <returns>The hash code.</returns> /// <returns>The hash code.</returns>
public override int GetHashCode() public override int GetHashCode()
{ {
int hash = 17; var hash = 17;
hash = 37 * hash + _timestamp.GetHashCode(); hash = 37 * hash + _timestamp.GetHashCode();
hash = 37 * hash + _machine.GetHashCode(); hash = 37 * hash + _machine.GetHashCode();
hash = 37 * hash + _pid.GetHashCode(); hash = 37 * hash + _pid.GetHashCode();
...@@ -460,21 +423,15 @@ namespace DotNetCore.CAP ...@@ -460,21 +423,15 @@ namespace DotNetCore.CAP
public static byte[] ParseHexString(string s) public static byte[] ParseHexString(string s)
{ {
if (s == null) if (s == null)
{
throw new ArgumentNullException("s"); throw new ArgumentNullException("s");
}
if (s.Length % 2 == 1) if (s.Length % 2 == 1)
{
throw new Exception("The binary key cannot have an odd number of digits"); throw new Exception("The binary key cannot have an odd number of digits");
}
byte[] arr = new byte[s.Length >> 1]; var arr = new byte[s.Length >> 1];
for (int i = 0; i < s.Length >> 1; ++i) for (var i = 0; i < s.Length >> 1; ++i)
{ arr[i] = (byte) ((GetHexVal(s[i << 1]) << 4) + GetHexVal(s[(i << 1) + 1]));
arr[i] = (byte)((GetHexVal(s[i << 1]) << 4) + (GetHexVal(s[(i << 1) + 1])));
}
return arr; return arr;
} }
...@@ -487,15 +444,13 @@ namespace DotNetCore.CAP ...@@ -487,15 +444,13 @@ namespace DotNetCore.CAP
public static string ToHexString(byte[] bytes) public static string ToHexString(byte[] bytes)
{ {
if (bytes == null) if (bytes == null)
{
throw new ArgumentNullException("bytes"); throw new ArgumentNullException("bytes");
}
var result = new char[bytes.Length * 2]; var result = new char[bytes.Length * 2];
for (int i = 0; i < bytes.Length; i++) for (var i = 0; i < bytes.Length; i++)
{ {
var val = _lookup32[bytes[i]]; var val = _lookup32[bytes[i]];
result[2 * i] = (char)val; result[2 * i] = (char) val;
result[2 * i + 1] = (char)(val >> 16); result[2 * i + 1] = (char) (val >> 16);
} }
return new string(result); return new string(result);
} }
...@@ -519,22 +474,15 @@ namespace DotNetCore.CAP ...@@ -519,22 +474,15 @@ namespace DotNetCore.CAP
public static DateTime ToUniversalTime(DateTime dateTime) public static DateTime ToUniversalTime(DateTime dateTime)
{ {
if (dateTime == DateTime.MinValue) if (dateTime == DateTime.MinValue)
{
return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
} if (dateTime == DateTime.MaxValue)
else if (dateTime == DateTime.MaxValue)
{
return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc); return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc);
}
else
{
return dateTime.ToUniversalTime(); return dateTime.ToUniversalTime();
} }
}
private static int GetHexVal(char hex) private static int GetHexVal(char hex)
{ {
int val = (int)hex; int val = hex;
//For uppercase A-F letters: //For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55); //return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters: //For lowercase a-f letters:
......
...@@ -21,7 +21,7 @@ namespace DotNetCore.CAP.Infrastructure ...@@ -21,7 +21,7 @@ namespace DotNetCore.CAP.Infrastructure
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
registeredHandle = ThreadPool.RegisterWaitForSingleObject( registeredHandle = ThreadPool.RegisterWaitForSingleObject(
handle, handle,
(state, timedOut) => ((TaskCompletionSource<bool>)state).TrySetResult(!timedOut), (state, timedOut) => ((TaskCompletionSource<bool>) state).TrySetResult(!timedOut),
tcs, tcs,
timeout, timeout,
true); true);
......
using System;
namespace DotNetCore.CAP.Infrastructure
{
public class WebHookProvider
{
public WebHookProvider()
{
throw new NotImplementedException();
}
}
}
\ No newline at end of file
...@@ -7,8 +7,8 @@ namespace DotNetCore.CAP.Internal ...@@ -7,8 +7,8 @@ namespace DotNetCore.CAP.Internal
public class ConsumerInvokerFactory : IConsumerInvokerFactory public class ConsumerInvokerFactory : IConsumerInvokerFactory
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
private readonly IModelBinderFactory _modelBinderFactory; private readonly IModelBinderFactory _modelBinderFactory;
private readonly IServiceProvider _serviceProvider;
public ConsumerInvokerFactory( public ConsumerInvokerFactory(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
......
...@@ -10,8 +10,7 @@ namespace DotNetCore.CAP.Internal ...@@ -10,8 +10,7 @@ namespace DotNetCore.CAP.Internal
public int CombinedHash public int CombinedHash
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _combinedHash64.GetHashCode(); }
get { return _combinedHash64.GetHashCode(); }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
...@@ -30,7 +29,7 @@ namespace DotNetCore.CAP.Internal ...@@ -30,7 +29,7 @@ namespace DotNetCore.CAP.Internal
else else
{ {
var count = 0; var count = 0;
foreach (object o in e) foreach (var o in e)
{ {
Add(o); Add(o);
count++; count++;
...@@ -54,14 +53,14 @@ namespace DotNetCore.CAP.Internal ...@@ -54,14 +53,14 @@ namespace DotNetCore.CAP.Internal
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(string s) public void Add(string s)
{ {
var hashCode = (s != null) ? s.GetHashCode() : 0; var hashCode = s != null ? s.GetHashCode() : 0;
Add(hashCode); Add(hashCode);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(object o) public void Add(object o)
{ {
var hashCode = (o != null) ? o.GetHashCode() : 0; var hashCode = o != null ? o.GetHashCode() : 0;
Add(hashCode); Add(hashCode);
} }
......
...@@ -11,11 +11,11 @@ namespace DotNetCore.CAP.Internal ...@@ -11,11 +11,11 @@ namespace DotNetCore.CAP.Internal
{ {
public class DefaultConsumerInvoker : IConsumerInvoker public class DefaultConsumerInvoker : IConsumerInvoker
{ {
private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
private readonly IModelBinderFactory _modelBinderFactory;
private readonly ConsumerContext _consumerContext; private readonly ConsumerContext _consumerContext;
private readonly ObjectMethodExecutor _executor; private readonly ObjectMethodExecutor _executor;
private readonly ILogger _logger;
private readonly IModelBinderFactory _modelBinderFactory;
private readonly IServiceProvider _serviceProvider;
public DefaultConsumerInvoker(ILogger logger, public DefaultConsumerInvoker(ILogger logger,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
...@@ -42,32 +42,24 @@ namespace DotNetCore.CAP.Internal ...@@ -42,32 +42,24 @@ namespace DotNetCore.CAP.Internal
var serviceType = _consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType(); var serviceType = _consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType();
var obj = ActivatorUtilities.GetServiceOrCreateInstance(provider, serviceType); var obj = ActivatorUtilities.GetServiceOrCreateInstance(provider, serviceType);
var jsonConent = _consumerContext.DeliverMessage.Content; var jsonContent = _consumerContext.DeliverMessage.Content;
var message = serializer.DeSerialize<CapMessageDto>(jsonConent); var message = serializer.DeSerialize<CapMessageDto>(jsonContent);
object result; object result;
if (_executor.MethodParameters.Length > 0) if (_executor.MethodParameters.Length > 0)
{
result = await ExecuteWithParameterAsync(obj, message.Content.ToString()); result = await ExecuteWithParameterAsync(obj, message.Content.ToString());
}
else else
{
result = await ExecuteAsync(obj); result = await ExecuteAsync(obj);
}
if (!string.IsNullOrEmpty(message.CallbackName)) if (!string.IsNullOrEmpty(message.CallbackName))
{
await SentCallbackMessage(message.Id, message.CallbackName, result); await SentCallbackMessage(message.Id, message.CallbackName, result);
} }
} }
}
private async Task<object> ExecuteAsync(object @class) private async Task<object> ExecuteAsync(object @class)
{ {
if (_executor.IsMethodAsync) if (_executor.IsMethodAsync)
{
return await _executor.ExecuteAsync(@class); return await _executor.ExecuteAsync(@class);
}
return _executor.Execute(@class); return _executor.Execute(@class);
} }
...@@ -81,16 +73,16 @@ namespace DotNetCore.CAP.Internal ...@@ -81,16 +73,16 @@ namespace DotNetCore.CAP.Internal
if (bindResult.IsSuccess) if (bindResult.IsSuccess)
{ {
if (_executor.IsMethodAsync) if (_executor.IsMethodAsync)
{
return await _executor.ExecuteAsync(@class, bindResult.Model); return await _executor.ExecuteAsync(@class, bindResult.Model);
}
return _executor.Execute(@class, bindResult.Model); return _executor.Execute(@class, bindResult.Model);
} }
throw new MethodBindException($"Parameters:{firstParameter.Name} bind failed! ParameterString is: {parameterString} "); throw new MethodBindException(
$"Parameters:{firstParameter.Name} bind failed! ParameterString is: {parameterString} ");
} }
catch (FormatException ex) catch (FormatException ex)
{ {
_logger.ModelBinderFormattingException(_executor.MethodInfo?.Name, firstParameter.Name, parameterString, ex); _logger.ModelBinderFormattingException(_executor.MethodInfo?.Name, firstParameter.Name, parameterString,
ex);
return null; return null;
} }
} }
......
...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.Internal ...@@ -17,7 +17,7 @@ namespace DotNetCore.CAP.Internal
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
/// <summary> /// <summary>
/// Creates a new <see cref="DefaultConsumerServiceSelector"/>. /// Creates a new <see cref="DefaultConsumerServiceSelector" />.
/// </summary> /// </summary>
public DefaultConsumerServiceSelector(IServiceProvider serviceProvider) public DefaultConsumerServiceSelector(IServiceProvider serviceProvider)
{ {
...@@ -25,7 +25,7 @@ namespace DotNetCore.CAP.Internal ...@@ -25,7 +25,7 @@ namespace DotNetCore.CAP.Internal
} }
/// <summary> /// <summary>
/// Selects the best <see cref="ConsumerExecutorDescriptor"/> candidate from <paramref name="executeDescriptor"/> for the /// Selects the best <see cref="ConsumerExecutorDescriptor" /> candidate from <paramref name="executeDescriptor" /> for the
/// current message associated. /// current message associated.
/// </summary> /// </summary>
public ConsumerExecutorDescriptor SelectBestCandidate(string key, public ConsumerExecutorDescriptor SelectBestCandidate(string key,
...@@ -58,9 +58,7 @@ namespace DotNetCore.CAP.Internal ...@@ -58,9 +58,7 @@ namespace DotNetCore.CAP.Internal
{ {
var typeInfo = service.GetType().GetTypeInfo(); var typeInfo = service.GetType().GetTypeInfo();
if (!typeof(ICapSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo)) if (!typeof(ICapSubscribe).GetTypeInfo().IsAssignableFrom(typeInfo))
{
continue; continue;
}
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo));
} }
...@@ -77,10 +75,8 @@ namespace DotNetCore.CAP.Internal ...@@ -77,10 +75,8 @@ namespace DotNetCore.CAP.Internal
{ {
var typeInfo = type.GetTypeInfo(); var typeInfo = type.GetTypeInfo();
if (Helper.IsController(typeInfo)) if (Helper.IsController(typeInfo))
{
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo));
} }
}
return executorDescriptorList; return executorDescriptorList;
} }
...@@ -89,23 +85,23 @@ namespace DotNetCore.CAP.Internal ...@@ -89,23 +85,23 @@ namespace DotNetCore.CAP.Internal
{ {
foreach (var method in typeInfo.DeclaredMethods) foreach (var method in typeInfo.DeclaredMethods)
{ {
var topicAttrs = method.GetCustomAttributes<TopicAttribute>(true); var topicAttr = method.GetCustomAttributes<TopicAttribute>(true);
if (!topicAttrs.Any()) continue; var topicAttributes = topicAttr as IList<TopicAttribute> ?? topicAttr.ToList();
foreach (var attr in topicAttrs) if (!topicAttributes.Any()) continue;
{
foreach (var attr in topicAttributes)
yield return InitDescriptor(attr, method, typeInfo); yield return InitDescriptor(attr, method, typeInfo);
} }
} }
}
private static ConsumerExecutorDescriptor InitDescriptor( private static ConsumerExecutorDescriptor InitDescriptor(
TopicAttribute attr, TopicAttribute attr,
MethodInfo methodInfo, MethodInfo methodInfo,
TypeInfo implType) TypeInfo implType)
{ {
var descriptor = new ConsumerExecutorDescriptor() var descriptor = new ConsumerExecutorDescriptor
{ {
Attribute = attr, Attribute = attr,
MethodInfo = methodInfo, MethodInfo = methodInfo,
......
...@@ -6,13 +6,13 @@ namespace DotNetCore.CAP.Internal ...@@ -6,13 +6,13 @@ namespace DotNetCore.CAP.Internal
{ {
public class JsonContentSerializer : IContentSerializer public class JsonContentSerializer : IContentSerializer
{ {
public T DeSerialize<T>(string messageObjStr) where T : CapMessageDto, new() { public T DeSerialize<T>(string messageObjStr) where T : CapMessageDto, new()
{
return Helper.FromJson<T>(messageObjStr); return Helper.FromJson<T>(messageObjStr);
} }
public string Serialize<T>(T messageObj) where T : CapMessageDto, new() { public string Serialize<T>(T messageObj) where T : CapMessageDto, new()
{
return Helper.ToJson(messageObj); return Helper.ToJson(messageObj);
} }
} }
......
...@@ -22,9 +22,7 @@ namespace DotNetCore.CAP.Internal ...@@ -22,9 +22,7 @@ namespace DotNetCore.CAP.Internal
public Task<ModelBindingResult> BindModelAsync(string content) public Task<ModelBindingResult> BindModelAsync(string content)
{ {
if (content == null) if (content == null)
{
throw new ArgumentNullException(nameof(content)); throw new ArgumentNullException(nameof(content));
}
var parameterType = _parameterInfo.ParameterType; var parameterType = _parameterInfo.ParameterType;
...@@ -32,47 +30,27 @@ namespace DotNetCore.CAP.Internal ...@@ -32,47 +30,27 @@ namespace DotNetCore.CAP.Internal
{ {
object model; object model;
if (parameterType == typeof(string)) if (parameterType == typeof(string))
{
if (string.IsNullOrWhiteSpace(content)) if (string.IsNullOrWhiteSpace(content))
{
model = null; model = null;
}
else else
{
model = content; model = content;
}
}
else if (string.IsNullOrWhiteSpace(content)) else if (string.IsNullOrWhiteSpace(content))
{
// Other than the StringConverter, converters Trim() the value then throw if the result is empty.
model = null; model = null;
}
else else
{
model = _typeConverter.ConvertFrom( model = _typeConverter.ConvertFrom(
context: null, null,
culture: CultureInfo.CurrentCulture, CultureInfo.CurrentCulture,
value: content); content);
}
if (model == null && !IsReferenceOrNullableType(parameterType)) if (model == null && !IsReferenceOrNullableType(parameterType))
{
return Task.FromResult(ModelBindingResult.Failed()); return Task.FromResult(ModelBindingResult.Failed());
}
else
{
return Task.FromResult(ModelBindingResult.Success(model)); return Task.FromResult(ModelBindingResult.Success(model));
} }
}
catch (Exception exception) catch (Exception exception)
{ {
var isFormatException = exception is FormatException; var isFormatException = exception is FormatException;
if (!isFormatException && exception.InnerException != null) if (!isFormatException && exception.InnerException != null)
{
// TypeConverter throws System.Exception wrapping the FormatException,
// so we capture the inner exception.
exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException;
}
throw; throw;
} }
} }
......
...@@ -10,14 +10,14 @@ namespace DotNetCore.CAP.Internal ...@@ -10,14 +10,14 @@ namespace DotNetCore.CAP.Internal
{ {
private readonly IConsumerServiceSelector _selector; private readonly IConsumerServiceSelector _selector;
private ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>> Entries { get; }
public MethodMatcherCache(IConsumerServiceSelector selector) public MethodMatcherCache(IConsumerServiceSelector selector)
{ {
_selector = selector; _selector = selector;
Entries = new ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>>(); Entries = new ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>>();
} }
private ConcurrentDictionary<string, IList<ConsumerExecutorDescriptor>> Entries { get; }
/// <summary> /// <summary>
/// Get a dictionary of candidates.In the dictionary, /// Get a dictionary of candidates.In the dictionary,
/// the Key is the CAPSubscribeAttribute Group, the Value for the current Group of candidates /// the Key is the CAPSubscribeAttribute Group, the Value for the current Group of candidates
...@@ -31,9 +31,7 @@ namespace DotNetCore.CAP.Internal ...@@ -31,9 +31,7 @@ namespace DotNetCore.CAP.Internal
var groupedCandidates = executorCollection.GroupBy(x => x.Attribute.Group); var groupedCandidates = executorCollection.GroupBy(x => x.Attribute.Group);
foreach (var item in groupedCandidates) foreach (var item in groupedCandidates)
{
Entries.TryAdd(item.Key, item.ToList()); Entries.TryAdd(item.Key, item.ToList());
}
return Entries; return Entries;
} }
...@@ -46,9 +44,7 @@ namespace DotNetCore.CAP.Internal ...@@ -46,9 +44,7 @@ namespace DotNetCore.CAP.Internal
public IDictionary<string, IList<ConsumerExecutorDescriptor>> GetTopicExector(string topicName) public IDictionary<string, IList<ConsumerExecutorDescriptor>> GetTopicExector(string topicName)
{ {
if (Entries == null) if (Entries == null)
{
throw new ArgumentNullException(nameof(Entries)); throw new ArgumentNullException(nameof(Entries));
}
var dic = new Dictionary<string, IList<ConsumerExecutorDescriptor>>(); var dic = new Dictionary<string, IList<ConsumerExecutorDescriptor>>();
foreach (var item in Entries) foreach (var item in Entries)
......
...@@ -8,7 +8,7 @@ using DotNetCore.CAP.Infrastructure; ...@@ -8,7 +8,7 @@ using DotNetCore.CAP.Infrastructure;
namespace DotNetCore.CAP.Internal namespace DotNetCore.CAP.Internal
{ {
/// <summary> /// <summary>
/// A factory for <see cref="IModelBinder"/> instances. /// A factory for <see cref="IModelBinder" /> instances.
/// </summary> /// </summary>
public class ModelBinderFactory : IModelBinderFactory public class ModelBinderFactory : IModelBinderFactory
{ {
...@@ -18,36 +18,25 @@ namespace DotNetCore.CAP.Internal ...@@ -18,36 +18,25 @@ namespace DotNetCore.CAP.Internal
public IModelBinder CreateBinder(ParameterInfo parameter) public IModelBinder CreateBinder(ParameterInfo parameter)
{ {
if (parameter == null) if (parameter == null)
{
throw new ArgumentNullException(nameof(parameter)); throw new ArgumentNullException(nameof(parameter));
}
object token = parameter; object token = parameter;
var binder = CreateBinderCoreCached(parameter, token); var binder = CreateBinderCoreCached(parameter, token);
if (binder == null) if (binder == null)
{
throw new InvalidOperationException("Format Could Not Create IModelBinder"); throw new InvalidOperationException("Format Could Not Create IModelBinder");
}
return binder; return binder;
} }
private IModelBinder CreateBinderCoreCached(ParameterInfo parameterInfo, object token) private IModelBinder CreateBinderCoreCached(ParameterInfo parameterInfo, object token)
{ {
IModelBinder binder; if (TryGetCachedBinder(parameterInfo, token, out var binder))
if (TryGetCachedBinder(parameterInfo, token, out binder))
{
return binder; return binder;
}
if (!Helper.IsComplexType(parameterInfo.ParameterType)) if (!Helper.IsComplexType(parameterInfo.ParameterType))
{
binder = new SimpleTypeModelBinder(parameterInfo); binder = new SimpleTypeModelBinder(parameterInfo);
}
else else
{
binder = new ComplexTypeModelBinder(parameterInfo); binder = new ComplexTypeModelBinder(parameterInfo);
}
AddToCache(parameterInfo, token, binder); AddToCache(parameterInfo, token, binder);
...@@ -57,9 +46,7 @@ namespace DotNetCore.CAP.Internal ...@@ -57,9 +46,7 @@ namespace DotNetCore.CAP.Internal
private void AddToCache(ParameterInfo info, object cacheToken, IModelBinder binder) private void AddToCache(ParameterInfo info, object cacheToken, IModelBinder binder)
{ {
if (cacheToken == null) if (cacheToken == null)
{
return; return;
}
_cache.TryAdd(new Key(info, cacheToken), binder); _cache.TryAdd(new Key(info, cacheToken), binder);
} }
......
...@@ -84,7 +84,8 @@ namespace Microsoft.Extensions.Internal ...@@ -84,7 +84,8 @@ namespace Microsoft.Extensions.Internal
&& m.GetParameters()[0].ParameterType == typeof(Action)); && m.GetParameters()[0].ParameterType == typeof(Action));
// Awaiter optionally implements ICriticalNotifyCompletion // Awaiter optionally implements ICriticalNotifyCompletion
var implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion)); var implementsICriticalNotifyCompletion =
awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));
MethodInfo unsafeOnCompletedMethod; MethodInfo unsafeOnCompletedMethod;
if (implementsICriticalNotifyCompletion) if (implementsICriticalNotifyCompletion)
{ {
......
...@@ -20,7 +20,8 @@ namespace Microsoft.Extensions.Internal ...@@ -20,7 +20,8 @@ namespace Microsoft.Extensions.Internal
CoercerResultType = null; CoercerResultType = null;
} }
public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo) public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType,
AwaitableInfo coercedAwaitableInfo)
{ {
CoercerExpression = coercerExpression; CoercerExpression = coercerExpression;
CoercerResultType = coercerResultType; CoercerResultType = coercerResultType;
...@@ -40,13 +41,11 @@ namespace Microsoft.Extensions.Internal ...@@ -40,13 +41,11 @@ namespace Microsoft.Extensions.Internal
if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type, if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type,
out var coercerExpression, out var coercerExpression,
out var coercerResultType)) out var coercerResultType))
{
if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo)) if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo))
{ {
info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo); info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo);
return true; return true;
} }
}
info = default(CoercedAwaitableInfo); info = default(CoercedAwaitableInfo);
return false; return false;
......
...@@ -10,12 +10,9 @@ namespace Microsoft.Extensions.Internal ...@@ -10,12 +10,9 @@ namespace Microsoft.Extensions.Internal
{ {
internal class ObjectMethodExecutor internal class ObjectMethodExecutor
{ {
private readonly object[] _parameterDefaultValues;
private readonly MethodExecutorAsync _executorAsync;
private readonly MethodExecutor _executor;
private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor = private static readonly ConstructorInfo _objectMethodExecutorAwaitableConstructor =
typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[] { typeof(ObjectMethodExecutorAwaitable).GetConstructor(new[]
{
typeof(object), // customAwaitable typeof(object), // customAwaitable
typeof(Func<object, object>), // getAwaiterMethod typeof(Func<object, object>), // getAwaiterMethod
typeof(Func<object, bool>), // isCompletedMethod typeof(Func<object, bool>), // isCompletedMethod
...@@ -24,12 +21,14 @@ namespace Microsoft.Extensions.Internal ...@@ -24,12 +21,14 @@ namespace Microsoft.Extensions.Internal
typeof(Action<object, Action>) // unsafeOnCompletedMethod typeof(Action<object, Action>) // unsafeOnCompletedMethod
}); });
private readonly MethodExecutor _executor;
private readonly MethodExecutorAsync _executorAsync;
private readonly object[] _parameterDefaultValues;
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues)
{ {
if (methodInfo == null) if (methodInfo == null)
{
throw new ArgumentNullException(nameof(methodInfo)); throw new ArgumentNullException(nameof(methodInfo));
}
MethodInfo = methodInfo; MethodInfo = methodInfo;
MethodParameters = methodInfo.GetParameters(); MethodParameters = methodInfo.GetParameters();
...@@ -47,19 +46,11 @@ namespace Microsoft.Extensions.Internal ...@@ -47,19 +46,11 @@ namespace Microsoft.Extensions.Internal
_executor = GetExecutor(methodInfo, targetTypeInfo); _executor = GetExecutor(methodInfo, targetTypeInfo);
if (IsMethodAsync) if (IsMethodAsync)
{
_executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo); _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo);
}
_parameterDefaultValues = parameterDefaultValues; _parameterDefaultValues = parameterDefaultValues;
} }
private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, params object[] parameters);
private delegate object MethodExecutor(object target, params object[] parameters);
private delegate void VoidMethodExecutor(object target, object[] parameters);
public MethodInfo MethodInfo { get; } public MethodInfo MethodInfo { get; }
public ParameterInfo[] MethodParameters { get; } public ParameterInfo[] MethodParameters { get; }
...@@ -78,18 +69,17 @@ namespace Microsoft.Extensions.Internal ...@@ -78,18 +69,17 @@ namespace Microsoft.Extensions.Internal
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null);
} }
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object[] parameterDefaultValues) public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo,
object[] parameterDefaultValues)
{ {
if (parameterDefaultValues == null) if (parameterDefaultValues == null)
{
throw new ArgumentNullException(nameof(parameterDefaultValues)); throw new ArgumentNullException(nameof(parameterDefaultValues));
}
return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues); return new ObjectMethodExecutor(methodInfo, targetTypeInfo, parameterDefaultValues);
} }
/// <summary> /// <summary>
/// Executes the configured method on <paramref name="target"/>. This can be used whether or not /// Executes the configured method on <paramref name="target" />. This can be used whether or not
/// the configured method is asynchronous. /// the configured method is asynchronous.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
...@@ -109,7 +99,7 @@ namespace Microsoft.Extensions.Internal ...@@ -109,7 +99,7 @@ namespace Microsoft.Extensions.Internal
} }
/// <summary> /// <summary>
/// Executes the configured method on <paramref name="target"/>. This can only be used if the configured /// Executes the configured method on <paramref name="target" />. This can only be used if the configured
/// method is asynchronous. /// method is asynchronous.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
...@@ -117,7 +107,6 @@ namespace Microsoft.Extensions.Internal ...@@ -117,7 +107,6 @@ namespace Microsoft.Extensions.Internal
/// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations /// which supplies an awaitable-of-object. This always works, but can incur several extra heap allocations
/// as compared with using Execute and then using "await" on the result value typecasted to the known /// as compared with using Execute and then using "await" on the result value typecasted to the known
/// awaitable type. The possible extra heap allocations are for: /// awaitable type. The possible extra heap allocations are for:
///
/// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally /// 1. The custom awaitable (though usually there's a heap allocation for this anyway, since normally
/// it's a reference type, and you normally create a new instance per call). /// it's a reference type, and you normally create a new instance per call).
/// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance /// 2. The custom awaiter (whether or not it's a value type, since if it's not, you need a new instance
...@@ -136,14 +125,11 @@ namespace Microsoft.Extensions.Internal ...@@ -136,14 +125,11 @@ namespace Microsoft.Extensions.Internal
public object GetDefaultValueForParameter(int index) public object GetDefaultValueForParameter(int index)
{ {
if (_parameterDefaultValues == null) if (_parameterDefaultValues == null)
{ throw new InvalidOperationException(
throw new InvalidOperationException($"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied."); $"Cannot call {nameof(GetDefaultValueForParameter)}, because no parameter default values were supplied.");
}
if (index < 0 || index > MethodParameters.Length - 1) if (index < 0 || index > MethodParameters.Length - 1)
{
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
}
return _parameterDefaultValues[index]; return _parameterDefaultValues[index];
} }
...@@ -157,7 +143,7 @@ namespace Microsoft.Extensions.Internal ...@@ -157,7 +143,7 @@ namespace Microsoft.Extensions.Internal
// Build parameter list // Build parameter list
var parameters = new List<Expression>(); var parameters = new List<Expression>();
var paramInfos = methodInfo.GetParameters(); var paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i++) for (var i = 0; i < paramInfos.Length; i++)
{ {
var paramInfo = paramInfos[i]; var paramInfo = paramInfos[i];
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
...@@ -190,7 +176,7 @@ namespace Microsoft.Extensions.Internal ...@@ -190,7 +176,7 @@ namespace Microsoft.Extensions.Internal
private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor) private static MethodExecutor WrapVoidMethod(VoidMethodExecutor executor)
{ {
return delegate (object target, object[] parameters) return delegate(object target, object[] parameters)
{ {
executor(target, parameters); executor(target, parameters);
return null; return null;
...@@ -209,7 +195,7 @@ namespace Microsoft.Extensions.Internal ...@@ -209,7 +195,7 @@ namespace Microsoft.Extensions.Internal
// Build parameter list // Build parameter list
var parameters = new List<Expression>(); var parameters = new List<Expression>();
var paramInfos = methodInfo.GetParameters(); var paramInfos = methodInfo.GetParameters();
for (int i = 0; i < paramInfos.Length; i++) for (var i = 0; i < paramInfos.Length; i++)
{ {
var paramInfo = paramInfos[i]; var paramInfo = paramInfos[i];
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
...@@ -253,12 +239,6 @@ namespace Microsoft.Extensions.Internal ...@@ -253,12 +239,6 @@ namespace Microsoft.Extensions.Internal
var getResultParam = Expression.Parameter(typeof(object), "awaiter"); var getResultParam = Expression.Parameter(typeof(object), "awaiter");
Func<object, object> getResultFunc; Func<object, object> getResultFunc;
if (awaitableInfo.ResultType == typeof(void)) if (awaitableInfo.ResultType == typeof(void))
{
// var getResultFunc = (object awaiter) =>
// {
// ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions
// return (object)null;
// };
getResultFunc = Expression.Lambda<Func<object, object>>( getResultFunc = Expression.Lambda<Func<object, object>>(
Expression.Block( Expression.Block(
Expression.Call( Expression.Call(
...@@ -267,11 +247,7 @@ namespace Microsoft.Extensions.Internal ...@@ -267,11 +247,7 @@ namespace Microsoft.Extensions.Internal
Expression.Constant(null) Expression.Constant(null)
), ),
getResultParam).Compile(); getResultParam).Compile();
}
else else
{
// var getResultFunc = (object awaiter) =>
// (object)((CustomAwaiterType)awaiter).GetResult();
getResultFunc = Expression.Lambda<Func<object, object>>( getResultFunc = Expression.Lambda<Func<object, object>>(
Expression.Convert( Expression.Convert(
Expression.Call( Expression.Call(
...@@ -279,7 +255,6 @@ namespace Microsoft.Extensions.Internal ...@@ -279,7 +255,6 @@ namespace Microsoft.Extensions.Internal
awaitableInfo.AwaiterGetResultMethod), awaitableInfo.AwaiterGetResultMethod),
typeof(object)), typeof(object)),
getResultParam).Compile(); getResultParam).Compile();
}
// var onCompletedFunc = (object awaiter, Action continuation) => { // var onCompletedFunc = (object awaiter, Action continuation) => {
// ((CustomAwaiterType)awaiter).OnCompleted(continuation); // ((CustomAwaiterType)awaiter).OnCompleted(continuation);
...@@ -315,7 +290,7 @@ namespace Microsoft.Extensions.Internal ...@@ -315,7 +290,7 @@ namespace Microsoft.Extensions.Internal
// awaitable, then do so. // awaitable, then do so.
var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion
? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall) ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall)
: (Expression)methodCall; : (Expression) methodCall;
// return new ObjectMethodExecutorAwaitable( // return new ObjectMethodExecutorAwaitable(
// (object)coercedMethodCall, // (object)coercedMethodCall,
...@@ -333,8 +308,15 @@ namespace Microsoft.Extensions.Internal ...@@ -333,8 +308,15 @@ namespace Microsoft.Extensions.Internal
Expression.Constant(onCompletedFunc), Expression.Constant(onCompletedFunc),
Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>))); Expression.Constant(unsafeOnCompletedFunc, typeof(Action<object, Action>)));
var lambda = Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter); var lambda =
Expression.Lambda<MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter);
return lambda.Compile(); return lambda.Compile();
} }
private delegate ObjectMethodExecutorAwaitable MethodExecutorAsync(object target, params object[] parameters);
private delegate object MethodExecutor(object target, params object[] parameters);
private delegate void VoidMethodExecutor(object target, object[] parameters);
} }
} }
\ No newline at end of file
...@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices; ...@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
namespace Microsoft.Extensions.Internal namespace Microsoft.Extensions.Internal
{ {
/// <summary> /// <summary>
/// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync"/> can /// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync" /> can
/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an /// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
/// application-defined custom awaitable. /// application-defined custom awaitable.
/// </summary> /// </summary>
...@@ -57,7 +57,8 @@ namespace Microsoft.Extensions.Internal ...@@ -57,7 +57,8 @@ namespace Microsoft.Extensions.Internal
public Awaiter GetAwaiter() public Awaiter GetAwaiter()
{ {
var customAwaiter = _getAwaiterMethod(_customAwaitable); var customAwaiter = _getAwaiterMethod(_customAwaitable);
return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod, _unsafeOnCompletedMethod); return new Awaiter(customAwaiter, _isCompletedMethod, _getResultMethod, _onCompletedMethod,
_unsafeOnCompletedMethod);
} }
public struct Awaiter : ICriticalNotifyCompletion public struct Awaiter : ICriticalNotifyCompletion
...@@ -84,7 +85,10 @@ namespace Microsoft.Extensions.Internal ...@@ -84,7 +85,10 @@ namespace Microsoft.Extensions.Internal
public bool IsCompleted => _isCompletedMethod(_customAwaiter); public bool IsCompleted => _isCompletedMethod(_customAwaiter);
public object GetResult() => _getResultMethod(_customAwaiter); public object GetResult()
{
return _getResultMethod(_customAwaiter);
}
public void OnCompleted(Action continuation) public void OnCompleted(Action continuation)
{ {
......
...@@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Internal ...@@ -12,7 +12,7 @@ namespace Microsoft.Extensions.Internal
{ {
/// <summary> /// <summary>
/// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying /// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying
/// an <see cref="Expression"/> for mapping instances of that type to a C# awaitable. /// an <see cref="Expression" /> for mapping instances of that type to a C# awaitable.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The main design goal here is to avoid taking a compile-time dependency on /// The main design goal here is to avoid taking a compile-time dependency on
...@@ -21,7 +21,7 @@ namespace Microsoft.Extensions.Internal ...@@ -21,7 +21,7 @@ namespace Microsoft.Extensions.Internal
/// </remarks> /// </remarks>
internal static class ObjectMethodExecutorFSharpSupport internal static class ObjectMethodExecutorFSharpSupport
{ {
private static object _fsharpValuesCacheLock = new object(); private static readonly object _fsharpValuesCacheLock = new object();
private static Assembly _fsharpCoreAssembly; private static Assembly _fsharpCoreAssembly;
private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod; private static MethodInfo _fsharpAsyncStartAsTaskGenericMethod;
private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty;
...@@ -73,26 +73,15 @@ namespace Microsoft.Extensions.Internal ...@@ -73,26 +73,15 @@ namespace Microsoft.Extensions.Internal
{ {
var typeFullName = possibleFSharpAsyncGenericType?.FullName; var typeFullName = possibleFSharpAsyncGenericType?.FullName;
if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal)) if (!string.Equals(typeFullName, "Microsoft.FSharp.Control.FSharpAsync`1", StringComparison.Ordinal))
{
return false; return false;
}
lock (_fsharpValuesCacheLock) lock (_fsharpValuesCacheLock)
{ {
if (_fsharpCoreAssembly != null) if (_fsharpCoreAssembly != null)
{
// Since we've already found the real FSharpAsync.Core assembly, we just have
// to check that the supplied FSharpAsync`1 type is the one from that assembly.
return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly; return possibleFSharpAsyncGenericType.Assembly == _fsharpCoreAssembly;
}
else
{
// We'll keep trying to find the FSharp types/values each time any type called
// FSharpAsync`1 is supplied.
return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType); return TryPopulateFSharpValueCaches(possibleFSharpAsyncGenericType);
} }
} }
}
private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType) private static bool TryPopulateFSharpValueCaches(Type possibleFSharpAsyncGenericType)
{ {
...@@ -101,9 +90,7 @@ namespace Microsoft.Extensions.Internal ...@@ -101,9 +90,7 @@ namespace Microsoft.Extensions.Internal
var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); var fsharpAsyncType = assembly.GetType("Microsoft.FSharp.Control.FSharpAsync");
if (fsharpOptionType == null || fsharpAsyncType == null) if (fsharpOptionType == null || fsharpAsyncType == null)
{
return false; return false;
}
// Get a reference to FSharpOption<TaskCreationOptions>.None // Get a reference to FSharpOption<TaskCreationOptions>.None
var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType var fsharpOptionOfTaskCreationOptionsType = fsharpOptionType
......
...@@ -14,6 +14,7 @@ namespace DotNetCore.CAP.Internal ...@@ -14,6 +14,7 @@ namespace DotNetCore.CAP.Internal
public SubscriberNotFoundException(string message, Exception inner) : public SubscriberNotFoundException(string message, Exception inner) :
base(message, inner) base(message, inner)
{ } {
}
} }
} }
\ No newline at end of file
...@@ -10,8 +10,8 @@ namespace DotNetCore.CAP ...@@ -10,8 +10,8 @@ namespace DotNetCore.CAP
private static readonly Action<ILogger, Exception> _serverShuttingDown; private static readonly Action<ILogger, Exception> _serverShuttingDown;
private static readonly Action<ILogger, string, Exception> _expectedOperationCanceledException; private static readonly Action<ILogger, string, Exception> _expectedOperationCanceledException;
private static readonly Action<ILogger, string, string, Exception> _enqueuingSentMessage; private static readonly Action<ILogger, string, string, Exception> _enqueueingSentMessage;
private static readonly Action<ILogger, string, string, Exception> _enqueuingReceivdeMessage; private static readonly Action<ILogger, string, string, Exception> _enqueueingReceivdeMessage;
private static readonly Action<ILogger, string, Exception> _executingConsumerMethod; private static readonly Action<ILogger, string, Exception> _executingConsumerMethod;
private static readonly Action<ILogger, string, Exception> _receivedMessageRetryExecuting; private static readonly Action<ILogger, string, Exception> _receivedMessageRetryExecuting;
private static readonly Action<ILogger, string, string, string, Exception> _modelBinderFormattingException; private static readonly Action<ILogger, string, string, string, Exception> _modelBinderFormattingException;
...@@ -44,12 +44,12 @@ namespace DotNetCore.CAP ...@@ -44,12 +44,12 @@ namespace DotNetCore.CAP
3, 3,
"Expected an OperationCanceledException, but found '{ExceptionMessage}'."); "Expected an OperationCanceledException, but found '{ExceptionMessage}'.");
_enqueuingSentMessage = LoggerMessage.Define<string, string>( _enqueueingSentMessage = LoggerMessage.Define<string, string>(
LogLevel.Debug, LogLevel.Debug,
2, 2,
"Enqueuing a topic to the sent message store. NameKey: '{NameKey}' Content: '{Content}'."); "Enqueuing a topic to the sent message store. NameKey: '{NameKey}' Content: '{Content}'.");
_enqueuingReceivdeMessage = LoggerMessage.Define<string, string>( _enqueueingReceivdeMessage = LoggerMessage.Define<string, string>(
LogLevel.Debug, LogLevel.Debug,
2, 2,
"Enqueuing a topic to the received message store. NameKey: '{NameKey}. Content: '{Content}'."); "Enqueuing a topic to the received message store. NameKey: '{NameKey}. Content: '{Content}'.");
...@@ -129,12 +129,12 @@ namespace DotNetCore.CAP ...@@ -129,12 +129,12 @@ namespace DotNetCore.CAP
public static void EnqueuingReceivedMessage(this ILogger logger, string nameKey, string content) public static void EnqueuingReceivedMessage(this ILogger logger, string nameKey, string content)
{ {
_enqueuingReceivdeMessage(logger, nameKey, content, null); _enqueueingReceivdeMessage(logger, nameKey, content, null);
} }
public static void EnqueuingSentMessage(this ILogger logger, string nameKey, string content) public static void EnqueuingSentMessage(this ILogger logger, string nameKey, string content)
{ {
_enqueuingSentMessage(logger, nameKey, content, null); _enqueueingSentMessage(logger, nameKey, content, null);
} }
public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount) public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount)
...@@ -162,7 +162,8 @@ namespace DotNetCore.CAP ...@@ -162,7 +162,8 @@ namespace DotNetCore.CAP
_exceptionOccuredWhileExecutingJob(logger, jobId, ex); _exceptionOccuredWhileExecutingJob(logger, jobId, ex);
} }
public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, string content, Exception ex) public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName,
string content, Exception ex)
{ {
_modelBinderFormattingException(logger, methodName, parameterName, content, ex); _modelBinderFormattingException(logger, methodName, parameterName, content, ex);
} }
......
...@@ -4,14 +4,6 @@ namespace DotNetCore.CAP.Models ...@@ -4,14 +4,6 @@ namespace DotNetCore.CAP.Models
{ {
public class CapMessageDto public class CapMessageDto
{ {
public virtual string Id { get; set; }
public virtual DateTime Timestamp { get; set; }
public virtual object Content { get; set; }
public virtual string CallbackName { get; set; }
public CapMessageDto() public CapMessageDto()
{ {
Id = ObjectId.GenerateNewStringId(); Id = ObjectId.GenerateNewStringId();
...@@ -22,5 +14,13 @@ namespace DotNetCore.CAP.Models ...@@ -22,5 +14,13 @@ namespace DotNetCore.CAP.Models
{ {
Content = content; Content = content;
} }
public virtual string Id { get; set; }
public virtual DateTime Timestamp { get; set; }
public virtual object Content { get; set; }
public virtual string CallbackName { get; set; }
} }
} }
\ No newline at end of file
...@@ -5,11 +5,8 @@ namespace DotNetCore.CAP.Models ...@@ -5,11 +5,8 @@ namespace DotNetCore.CAP.Models
public class CapPublishedMessage public class CapPublishedMessage
{ {
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="CapPublishedMessage"/>. /// Initializes a new instance of <see cref="CapPublishedMessage" />.
/// </summary> /// </summary>
/// <remarks>
/// The Id property is initialized to from a new GUID string value.
/// </remarks>
public CapPublishedMessage() public CapPublishedMessage()
{ {
Added = DateTime.Now; Added = DateTime.Now;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
public int MessageId { get; set; } public int MessageId { get; set; }
/// <summary> /// <summary>
/// 0 is CapSentMessage, 1 is CapReceviedMessage /// 0 is CapSentMessage, 1 is CapReceivedMessage
/// </summary> /// </summary>
public MessageType MessageType { get; set; } public MessageType MessageType { get; set; }
} }
......
...@@ -5,11 +5,8 @@ namespace DotNetCore.CAP.Models ...@@ -5,11 +5,8 @@ namespace DotNetCore.CAP.Models
public class CapReceivedMessage public class CapReceivedMessage
{ {
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="CapReceivedMessage"/>. /// Initializes a new instance of <see cref="CapReceivedMessage" />.
/// </summary> /// </summary>
/// <remarks>
/// The Id property is initialized to from a new GUID string value.
/// </remarks>
public CapReceivedMessage() public CapReceivedMessage()
{ {
Added = DateTime.Now; Added = DateTime.Now;
......
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
public class DiscoveryOptions public class DiscoveryOptions
{ {
public const string DefaultDiscoveryServerHost = "localhost"; public const string DefaultDiscoveryServerHost = "localhost";
public const int DefaultDiscoveryServerProt = 8500; public const int DefaultDiscoveryServerPort = 8500;
public const string DefaultCurrentNodeHostName = "localhost"; public const string DefaultCurrentNodeHostName = "localhost";
public const int DefaultCurrentNodePort = 5000; public const int DefaultCurrentNodePort = 5000;
...@@ -14,7 +15,7 @@ namespace DotNetCore.CAP ...@@ -14,7 +15,7 @@ namespace DotNetCore.CAP
public DiscoveryOptions() public DiscoveryOptions()
{ {
DiscoveryServerHostName = DefaultDiscoveryServerHost; DiscoveryServerHostName = DefaultDiscoveryServerHost;
DiscoveryServerProt = DefaultDiscoveryServerProt; DiscoveryServerPort = DefaultDiscoveryServerPort;
CurrentNodeHostName = DefaultCurrentNodeHostName; CurrentNodeHostName = DefaultCurrentNodeHostName;
CurrentNodePort = DefaultCurrentNodePort; CurrentNodePort = DefaultCurrentNodePort;
...@@ -23,7 +24,7 @@ namespace DotNetCore.CAP ...@@ -23,7 +24,7 @@ namespace DotNetCore.CAP
} }
public string DiscoveryServerHostName { get; set; } public string DiscoveryServerHostName { get; set; }
public int DiscoveryServerProt { get; set; } public int DiscoveryServerPort { get; set; }
public string CurrentNodeHostName { get; set; } public string CurrentNodeHostName { get; set; }
public int CurrentNodePort { get; set; } public int CurrentNodePort { get; set; }
...@@ -32,5 +33,4 @@ namespace DotNetCore.CAP ...@@ -32,5 +33,4 @@ namespace DotNetCore.CAP
public string NodeName { get; set; } public string NodeName { get; set; }
public string MatchPath { get; set; } public string MatchPath { get; set; }
} }
} }
\ No newline at end of file
using System; using System;
using DotNetCore.CAP;
using DotNetCore.CAP.NodeDiscovery;
using Microsoft.Extensions.DependencyInjection;
namespace DotNetCore.CAP namespace DotNetCore.CAP
{ {
using NodeDiscovery;
using Microsoft.Extensions.DependencyInjection;
internal sealed class DiscoveryOptionsExtension : ICapOptionsExtension internal sealed class DiscoveryOptionsExtension : ICapOptionsExtension
{ {
private readonly Action<DiscoveryOptions> _options; private readonly Action<DiscoveryOptions> _options;
...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP ...@@ -23,7 +23,7 @@ namespace DotNetCore.CAP
services.AddSingleton<IDiscoveryProviderFactory, DiscoveryProviderFactory>(); services.AddSingleton<IDiscoveryProviderFactory, DiscoveryProviderFactory>();
services.AddSingleton<IProcessingServer, ConsulProcessingNodeServer>(); services.AddSingleton<IProcessingServer, ConsulProcessingNodeServer>();
services.AddSingleton<INodeDiscoveryProvider>(x => services.AddSingleton(x =>
{ {
var configOptions = x.GetService<DiscoveryOptions>(); var configOptions = x.GetService<DiscoveryOptions>();
var factory = x.GetService<IDiscoveryProviderFactory>(); var factory = x.GetService<IDiscoveryProviderFactory>();
...@@ -35,8 +35,6 @@ namespace DotNetCore.CAP ...@@ -35,8 +35,6 @@ namespace DotNetCore.CAP
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
{ {
using DotNetCore.CAP;
public static class CapDiscoveryOptionsExtensions public static class CapDiscoveryOptionsExtensions
{ {
public static CapOptions UseDiscovery(this CapOptions capOptions) public static CapOptions UseDiscovery(this CapOptions capOptions)
......
...@@ -7,9 +7,7 @@ namespace DotNetCore.CAP.NodeDiscovery ...@@ -7,9 +7,7 @@ namespace DotNetCore.CAP.NodeDiscovery
public INodeDiscoveryProvider Create(DiscoveryOptions options) public INodeDiscoveryProvider Create(DiscoveryOptions options)
{ {
if (options == null) if (options == null)
{
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
}
return new ConsulNodeDiscoveryProvider(options); return new ConsulNodeDiscoveryProvider(options);
} }
......
using System; using System;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Consul; using Consul;
...@@ -8,8 +8,8 @@ namespace DotNetCore.CAP.NodeDiscovery ...@@ -8,8 +8,8 @@ namespace DotNetCore.CAP.NodeDiscovery
{ {
public class ConsulNodeDiscoveryProvider : INodeDiscoveryProvider, IDisposable public class ConsulNodeDiscoveryProvider : INodeDiscoveryProvider, IDisposable
{ {
private ConsulClient _consul;
private readonly DiscoveryOptions _options; private readonly DiscoveryOptions _options;
private ConsulClient _consul;
public ConsulNodeDiscoveryProvider(DiscoveryOptions options) public ConsulNodeDiscoveryProvider(DiscoveryOptions options)
{ {
...@@ -18,21 +18,19 @@ namespace DotNetCore.CAP.NodeDiscovery ...@@ -18,21 +18,19 @@ namespace DotNetCore.CAP.NodeDiscovery
InitClient(); InitClient();
} }
public void InitClient() public void Dispose()
{
_consul = new ConsulClient(config =>
{ {
config.WaitTime = TimeSpan.FromSeconds(5); _consul.Dispose();
config.Address = new Uri($"http://{_options.DiscoveryServerHostName}:{_options.DiscoveryServerProt}");
});
} }
public async Task<IList<Node>> GetNodes() public async Task<IList<Node>> GetNodes()
{ {
try { try
{
var services = await _consul.Agent.Services(); var services = await _consul.Agent.Services();
var nodes = services.Response.Select(x => new Node { var nodes = services.Response.Select(x => new Node
{
Id = x.Key, Id = x.Key,
Name = x.Value.Service, Name = x.Value.Service,
Address = x.Value.Address, Address = x.Value.Address,
...@@ -41,11 +39,12 @@ namespace DotNetCore.CAP.NodeDiscovery ...@@ -41,11 +39,12 @@ namespace DotNetCore.CAP.NodeDiscovery
}); });
var nodeList = nodes.ToList(); var nodeList = nodes.ToList();
CapCache.Global.AddOrUpdate("cap.nodes.count", nodeList.Count, TimeSpan.FromSeconds(30),true); CapCache.Global.AddOrUpdate("cap.nodes.count", nodeList.Count, TimeSpan.FromSeconds(30), true);
return nodeList; return nodeList;
} }
catch (Exception) { catch (Exception)
{
return null; return null;
} }
} }
...@@ -58,20 +57,25 @@ namespace DotNetCore.CAP.NodeDiscovery ...@@ -58,20 +57,25 @@ namespace DotNetCore.CAP.NodeDiscovery
Name = _options.NodeName, Name = _options.NodeName,
Address = _options.CurrentNodeHostName, Address = _options.CurrentNodeHostName,
Port = _options.CurrentNodePort, Port = _options.CurrentNodePort,
Tags = new[] { "CAP", "Client", "Dashboard" }, Tags = new[] {"CAP", "Client", "Dashboard"},
Check = new AgentServiceCheck Check = new AgentServiceCheck
{ {
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30), DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(30),
Interval = TimeSpan.FromSeconds(10), Interval = TimeSpan.FromSeconds(10),
Status = HealthStatus.Passing, Status = HealthStatus.Passing,
HTTP = $"http://{_options.CurrentNodeHostName}:{_options.CurrentNodePort}{_options.MatchPath}/health" HTTP =
$"http://{_options.CurrentNodeHostName}:{_options.CurrentNodePort}{_options.MatchPath}/health"
} }
}); });
} }
public void Dispose() public void InitClient()
{ {
_consul.Dispose(); _consul = new ConsulClient(config =>
{
config.WaitTime = TimeSpan.FromSeconds(5);
config.Address = new Uri($"http://{_options.DiscoveryServerHostName}:{_options.DiscoveryServerPort}");
});
} }
} }
} }
\ No newline at end of file
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
public void Dispose() public void Dispose()
{ {
} }
} }
} }
\ No newline at end of file
...@@ -22,30 +22,32 @@ namespace DotNetCore.CAP ...@@ -22,30 +22,32 @@ namespace DotNetCore.CAP
public Exception Exception { get; set; } public Exception Exception { get; set; }
/// <summary> /// <summary>
/// An <see cref="IEnumerable{T}"/> of <see cref="OperateError"/>s containing an errors /// An <see cref="IEnumerable{T}" /> of <see cref="OperateError" />s containing an errors
/// that occurred during the operation. /// that occurred during the operation.
/// </summary> /// </summary>
/// <value>An <see cref="IEnumerable{T}"/> of <see cref="OperateError"/>s.</value> /// <value>An <see cref="IEnumerable{T}" /> of <see cref="OperateError" />s.</value>
public IEnumerable<OperateError> Errors => _errors; public IEnumerable<OperateError> Errors => _errors;
/// <summary> /// <summary>
/// Returns an <see cref="OperateResult"/> indicating a successful identity operation. /// Returns an <see cref="OperateResult" /> indicating a successful identity operation.
/// </summary> /// </summary>
/// <returns>An <see cref="OperateResult"/> indicating a successful operation.</returns> /// <returns>An <see cref="OperateResult" /> indicating a successful operation.</returns>
public static OperateResult Success { get; } = new OperateResult { Succeeded = true }; public static OperateResult Success { get; } = new OperateResult {Succeeded = true};
/// <summary> /// <summary>
/// Creates an <see cref="OperateResult"/> indicating a failed operation, with a list of <paramref name="errors"/> if applicable. /// Creates an <see cref="OperateResult" /> indicating a failed operation, with a list of <paramref name="errors" /> if
/// applicable.
/// </summary> /// </summary>
/// <param name="errors">An optional array of <see cref="OperateError"/>s which caused the operation to fail.</param> /// <param name="errors">An optional array of <see cref="OperateError" />s which caused the operation to fail.</param>
/// <returns>An <see cref="OperateResult"/> indicating a failed operation, with a list of <paramref name="errors"/> if applicable.</returns> /// <returns>
/// An <see cref="OperateResult" /> indicating a failed operation, with a list of <paramref name="errors" /> if
/// applicable.
/// </returns>
public static OperateResult Failed(params OperateError[] errors) public static OperateResult Failed(params OperateError[] errors)
{ {
var result = new OperateResult { Succeeded = false }; var result = new OperateResult {Succeeded = false};
if (errors != null) if (errors != null)
{
result._errors.AddRange(errors); result._errors.AddRange(errors);
}
return result; return result;
} }
...@@ -57,19 +59,17 @@ namespace DotNetCore.CAP ...@@ -57,19 +59,17 @@ namespace DotNetCore.CAP
Exception = ex Exception = ex
}; };
if (errors != null) if (errors != null)
{
result._errors.AddRange(errors); result._errors.AddRange(errors);
}
return result; return result;
} }
/// <summary> /// <summary>
/// Converts the value of the current <see cref="OperateResult"/> object to its equivalent string representation. /// Converts the value of the current <see cref="OperateResult" /> object to its equivalent string representation.
/// </summary> /// </summary>
/// <returns>A string representation of the current <see cref="OperateResult"/> object.</returns> /// <returns>A string representation of the current <see cref="OperateResult" /> object.</returns>
/// <remarks> /// <remarks>
/// If the operation was successful the ToString() will return "Succeeded" otherwise it returned /// If the operation was successful the ToString() will return "Succeeded" otherwise it returned
/// "Failed : " followed by a comma delimited list of error codes from its <see cref="Errors"/> collection, if any. /// "Failed : " followed by a comma delimited list of error codes from its <see cref="Errors" /> collection, if any.
/// </remarks> /// </remarks>
public override string ToString() public override string ToString()
{ {
......
...@@ -9,11 +9,10 @@ namespace DotNetCore.CAP.Processor ...@@ -9,11 +9,10 @@ namespace DotNetCore.CAP.Processor
{ {
public class DefaultDispatcher : IDispatcher public class DefaultDispatcher : IDispatcher
{ {
private readonly IQueueExecutorFactory _queueExecutorFactory; internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true);
private readonly TimeSpan _pollingDelay; private readonly TimeSpan _pollingDelay;
private readonly IQueueExecutorFactory _queueExecutorFactory;
internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true);
public DefaultDispatcher(IQueueExecutorFactory queueExecutorFactory, public DefaultDispatcher(IQueueExecutorFactory queueExecutorFactory,
IOptions<CapOptions> capOptions) IOptions<CapOptions> capOptions)
...@@ -70,14 +69,12 @@ namespace DotNetCore.CAP.Processor ...@@ -70,14 +69,12 @@ namespace DotNetCore.CAP.Processor
var connection = provider.GetRequiredService<IStorageConnection>(); var connection = provider.GetRequiredService<IStorageConnection>();
if ((fetched = await connection.FetchNextMessageAsync()) != null) if ((fetched = await connection.FetchNextMessageAsync()) != null)
{
using (fetched) using (fetched)
{ {
var queueExecutor = _queueExecutorFactory.GetInstance(fetched.MessageType); var queueExecutor = _queueExecutorFactory.GetInstance(fetched.MessageType);
await queueExecutor.ExecuteAsync(connection, fetched); await queueExecutor.ExecuteAsync(connection, fetched);
} }
} }
}
return fetched != null; return fetched != null;
} }
} }
......
...@@ -11,18 +11,18 @@ namespace DotNetCore.CAP.Processor ...@@ -11,18 +11,18 @@ namespace DotNetCore.CAP.Processor
{ {
public class CapProcessingServer : IProcessingServer public class CapProcessingServer : IProcessingServer
{ {
private readonly CancellationTokenSource _cts;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private readonly IServiceProvider _provider;
private readonly CancellationTokenSource _cts;
private readonly CapOptions _options;
private readonly IList<IDispatcher> _messageDispatchers; private readonly IList<IDispatcher> _messageDispatchers;
private readonly CapOptions _options;
private IProcessor[] _processors; private readonly IServiceProvider _provider;
private ProcessingContext _context;
private Task _compositeTask; private Task _compositeTask;
private ProcessingContext _context;
private bool _disposed; private bool _disposed;
private IProcessor[] _processors;
public CapProcessingServer( public CapProcessingServer(
ILogger<CapProcessingServer> logger, ILogger<CapProcessingServer> logger,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
...@@ -55,10 +55,7 @@ namespace DotNetCore.CAP.Processor ...@@ -55,10 +55,7 @@ namespace DotNetCore.CAP.Processor
public void Pulse() public void Pulse()
{ {
if (!AllProcessorsWaiting()) if (!AllProcessorsWaiting())
{
// Some processor is still executing jobs so no need to pulse.
return; return;
}
_logger.LogTrace("Pulsing the Queuer."); _logger.LogTrace("Pulsing the Queuer.");
...@@ -68,36 +65,28 @@ namespace DotNetCore.CAP.Processor ...@@ -68,36 +65,28 @@ namespace DotNetCore.CAP.Processor
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
{
return; return;
}
_disposed = true; _disposed = true;
_logger.ServerShuttingDown(); _logger.ServerShuttingDown();
_cts.Cancel(); _cts.Cancel();
try try
{ {
_compositeTask.Wait((int)TimeSpan.FromSeconds(10).TotalMilliseconds); _compositeTask.Wait((int) TimeSpan.FromSeconds(10).TotalMilliseconds);
} }
catch (AggregateException ex) catch (AggregateException ex)
{ {
var innerEx = ex.InnerExceptions[0]; var innerEx = ex.InnerExceptions[0];
if (!(innerEx is OperationCanceledException)) if (!(innerEx is OperationCanceledException))
{
_logger.ExpectedOperationCanceledException(innerEx); _logger.ExpectedOperationCanceledException(innerEx);
} }
} }
}
private bool AllProcessorsWaiting() private bool AllProcessorsWaiting()
{ {
foreach (var processor in _messageDispatchers) foreach (var processor in _messageDispatchers)
{
if (!processor.Waiting) if (!processor.Waiting)
{
return false; return false;
}
}
return true; return true;
} }
......
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor.States; using DotNetCore.CAP.Processor.States;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
...@@ -9,12 +10,11 @@ namespace DotNetCore.CAP.Processor ...@@ -9,12 +10,11 @@ namespace DotNetCore.CAP.Processor
{ {
public class FailedJobProcessor : IProcessor public class FailedJobProcessor : IProcessor
{ {
private readonly CapOptions _options; private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly CapOptions _options;
private readonly IServiceProvider _provider; private readonly IServiceProvider _provider;
private readonly IStateChanger _stateChanger; private readonly IStateChanger _stateChanger;
private readonly TimeSpan _delay = TimeSpan.FromSeconds(1);
private readonly TimeSpan _waitingInterval; private readonly TimeSpan _waitingInterval;
public FailedJobProcessor( public FailedJobProcessor(
...@@ -42,7 +42,7 @@ namespace DotNetCore.CAP.Processor ...@@ -42,7 +42,7 @@ namespace DotNetCore.CAP.Processor
await Task.WhenAll( await Task.WhenAll(
ProcessPublishedAsync(connection, context), ProcessPublishedAsync(connection, context),
ProcessReceivededAsync(connection, context)); ProcessReceivedAsync(connection, context));
DefaultDispatcher.PulseEvent.Set(); DefaultDispatcher.PulseEvent.Set();
...@@ -58,18 +58,15 @@ namespace DotNetCore.CAP.Processor ...@@ -58,18 +58,15 @@ namespace DotNetCore.CAP.Processor
foreach (var message in messages) foreach (var message in messages)
{ {
if (!hasException) if (!hasException)
{
try try
{ {
_options.FailedCallback?.Invoke(Models.MessageType.Publish, message.Name, message.Content); _options.FailedCallback?.Invoke(MessageType.Publish, message.Name, message.Content);
} }
catch (Exception ex) catch (Exception ex)
{ {
hasException = true; hasException = true;
_logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message);
} }
}
using (var transaction = connection.CreateTransaction()) using (var transaction = connection.CreateTransaction())
{ {
...@@ -83,26 +80,23 @@ namespace DotNetCore.CAP.Processor ...@@ -83,26 +80,23 @@ namespace DotNetCore.CAP.Processor
} }
} }
private async Task ProcessReceivededAsync(IStorageConnection connection, ProcessingContext context) private async Task ProcessReceivedAsync(IStorageConnection connection, ProcessingContext context)
{ {
var messages = await connection.GetFailedReceviedMessages(); var messages = await connection.GetFailedReceivedMessages();
var hasException = false; var hasException = false;
foreach (var message in messages) foreach (var message in messages)
{ {
if (!hasException) if (!hasException)
{
try try
{ {
_options.FailedCallback?.Invoke(Models.MessageType.Subscribe, message.Name, message.Content); _options.FailedCallback?.Invoke(MessageType.Subscribe, message.Name, message.Content);
} }
catch (Exception ex) catch (Exception ex)
{ {
hasException = true; hasException = true;
_logger.LogWarning("Failed call-back method raised an exception:" + ex.Message); _logger.LogWarning("Failed call-back method raised an exception:" + ex.Message);
} }
}
using (var transaction = connection.CreateTransaction()) using (var transaction = connection.CreateTransaction())
{ {
......
...@@ -17,12 +17,9 @@ namespace DotNetCore.CAP.Processor ...@@ -17,12 +17,9 @@ namespace DotNetCore.CAP.Processor
_logger = loggerFactory.CreateLogger<InfiniteRetryProcessor>(); _logger = loggerFactory.CreateLogger<InfiniteRetryProcessor>();
} }
public override string ToString() => _inner.ToString();
public async Task ProcessAsync(ProcessingContext context) public async Task ProcessAsync(ProcessingContext context)
{ {
while (!context.IsStopping) while (!context.IsStopping)
{
try try
{ {
await _inner.ProcessAsync(context); await _inner.ProcessAsync(context);
...@@ -33,9 +30,13 @@ namespace DotNetCore.CAP.Processor ...@@ -33,9 +30,13 @@ namespace DotNetCore.CAP.Processor
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogWarning(1, ex, "Prcessor '{ProcessorName}' failed. Retrying...", _inner.ToString()); _logger.LogWarning(1, ex, "Processor '{ProcessorName}' failed. Retrying...", _inner.ToString());
} }
} }
public override string ToString()
{
return _inner.ToString();
} }
} }
} }
\ No newline at end of file
...@@ -12,12 +12,11 @@ namespace DotNetCore.CAP.Processor ...@@ -12,12 +12,11 @@ namespace DotNetCore.CAP.Processor
{ {
public class PublishQueuer : IProcessor public class PublishQueuer : IProcessor
{ {
public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true);
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IStateChanger _stateChanger;
private readonly IServiceProvider _provider;
private readonly TimeSpan _pollingDelay; private readonly TimeSpan _pollingDelay;
private readonly IServiceProvider _provider;
public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); private readonly IStateChanger _stateChanger;
public PublishQueuer( public PublishQueuer(
ILogger<PublishQueuer> logger, ILogger<PublishQueuer> logger,
......
...@@ -12,12 +12,11 @@ namespace DotNetCore.CAP.Processor ...@@ -12,12 +12,11 @@ namespace DotNetCore.CAP.Processor
{ {
public class SubscribeQueuer : IProcessor public class SubscribeQueuer : IProcessor
{ {
internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true);
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IStateChanger _stateChanger;
private readonly IServiceProvider _provider;
private readonly TimeSpan _pollingDelay; private readonly TimeSpan _pollingDelay;
private readonly IServiceProvider _provider;
internal static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); private readonly IStateChanger _stateChanger;
public SubscribeQueuer( public SubscribeQueuer(
ILogger<SubscribeQueuer> logger, ILogger<SubscribeQueuer> logger,
...@@ -44,7 +43,7 @@ namespace DotNetCore.CAP.Processor ...@@ -44,7 +43,7 @@ namespace DotNetCore.CAP.Processor
while ( while (
!context.IsStopping && !context.IsStopping &&
(message = await connection.GetNextReceviedMessageToBeEnqueuedAsync()) != null) (message = await connection.GetNextReceivedMessageToBeEnqueuedAsync()) != null)
{ {
var state = new EnqueuedState(); var state = new EnqueuedState();
......
...@@ -33,7 +33,15 @@ namespace DotNetCore.CAP.Processor ...@@ -33,7 +33,15 @@ namespace DotNetCore.CAP.Processor
public bool IsStopping => CancellationToken.IsCancellationRequested; public bool IsStopping => CancellationToken.IsCancellationRequested;
public void ThrowIfStopping() => CancellationToken.ThrowIfCancellationRequested(); public void Dispose()
{
_scope?.Dispose();
}
public void ThrowIfStopping()
{
CancellationToken.ThrowIfCancellationRequested();
}
public ProcessingContext CreateScope() public ProcessingContext CreateScope()
{ {
...@@ -50,10 +58,5 @@ namespace DotNetCore.CAP.Processor ...@@ -50,10 +58,5 @@ namespace DotNetCore.CAP.Processor
{ {
return Task.Delay(timeout, CancellationToken); return Task.Delay(timeout, CancellationToken);
} }
public void Dispose()
{
_scope?.Dispose();
}
} }
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ namespace DotNetCore.CAP.Processor ...@@ -10,6 +10,7 @@ namespace DotNetCore.CAP.Processor
public static readonly RetryBehavior DefaultRetry; public static readonly RetryBehavior DefaultRetry;
public static readonly RetryBehavior NoRetry; public static readonly RetryBehavior NoRetry;
// ReSharper disable once InconsistentNaming
private static readonly Random _random = new Random(); private static readonly Random _random = new Random();
private readonly Func<int, int> _retryInThunk; private readonly Func<int, int> _retryInThunk;
...@@ -18,7 +19,7 @@ namespace DotNetCore.CAP.Processor ...@@ -18,7 +19,7 @@ namespace DotNetCore.CAP.Processor
{ {
DefaultRetryCount = 15; DefaultRetryCount = 15;
DefaultRetryInThunk = retries => DefaultRetryInThunk = retries =>
(int)Math.Round(Math.Pow(retries - 1, 4) + 15 + (_random.Next(30) * (retries))); (int) Math.Round(Math.Pow(retries - 1, 4) + 15 + _random.Next(30) * retries);
DefaultRetry = new RetryBehavior(true); DefaultRetry = new RetryBehavior(true);
NoRetry = new RetryBehavior(false); NoRetry = new RetryBehavior(false);
...@@ -30,7 +31,7 @@ namespace DotNetCore.CAP.Processor ...@@ -30,7 +31,7 @@ namespace DotNetCore.CAP.Processor
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RetryBehavior"/> class. /// Initializes a new instance of the <see cref="RetryBehavior" /> class.
/// </summary> /// </summary>
/// <param name="retry">Whether to retry.</param> /// <param name="retry">Whether to retry.</param>
/// <param name="retryCount">The maximum retry count.</param> /// <param name="retryCount">The maximum retry count.</param>
...@@ -38,9 +39,7 @@ namespace DotNetCore.CAP.Processor ...@@ -38,9 +39,7 @@ namespace DotNetCore.CAP.Processor
public RetryBehavior(bool retry, int retryCount, Func<int, int> retryInThunk) public RetryBehavior(bool retry, int retryCount, Func<int, int> retryInThunk)
{ {
if (retry) if (retry)
{
if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative.");
}
Retry = retry; Retry = retry;
RetryCount = retryCount; RetryCount = retryCount;
......
...@@ -7,10 +7,6 @@ namespace DotNetCore.CAP.Processor.States ...@@ -7,10 +7,6 @@ namespace DotNetCore.CAP.Processor.States
{ {
public const string StateName = "Succeeded"; public const string StateName = "Succeeded";
public TimeSpan? ExpiresAfter { get; }
public string Name => StateName;
public SucceededState() public SucceededState()
{ {
ExpiresAfter = TimeSpan.FromHours(1); ExpiresAfter = TimeSpan.FromHours(1);
...@@ -21,6 +17,10 @@ namespace DotNetCore.CAP.Processor.States ...@@ -21,6 +17,10 @@ namespace DotNetCore.CAP.Processor.States
ExpiresAfter = TimeSpan.FromSeconds(ExpireAfterSeconds); ExpiresAfter = TimeSpan.FromSeconds(ExpireAfterSeconds);
} }
public TimeSpan? ExpiresAfter { get; }
public string Name => StateName;
public void Apply(CapPublishedMessage message, IStorageTransaction transaction) public void Apply(CapPublishedMessage message, IStorageTransaction transaction)
{ {
} }
......
...@@ -9,13 +9,9 @@ namespace DotNetCore.CAP.Processor.States ...@@ -9,13 +9,9 @@ namespace DotNetCore.CAP.Processor.States
{ {
var now = DateTime.Now; var now = DateTime.Now;
if (state.ExpiresAfter != null) if (state.ExpiresAfter != null)
{
message.ExpiresAt = now.Add(state.ExpiresAfter.Value); message.ExpiresAt = now.Add(state.ExpiresAfter.Value);
}
else else
{
message.ExpiresAt = null; message.ExpiresAt = null;
}
message.StatusName = state.Name; message.StatusName = state.Name;
state.Apply(message, transaction); state.Apply(message, transaction);
...@@ -26,13 +22,9 @@ namespace DotNetCore.CAP.Processor.States ...@@ -26,13 +22,9 @@ namespace DotNetCore.CAP.Processor.States
{ {
var now = DateTime.Now; var now = DateTime.Now;
if (state.ExpiresAfter != null) if (state.ExpiresAfter != null)
{
message.ExpiresAt = now.Add(state.ExpiresAfter.Value); message.ExpiresAt = now.Add(state.ExpiresAfter.Value);
}
else else
{
message.ExpiresAt = null; message.ExpiresAt = null;
}
message.StatusName = state.Name; message.StatusName = state.Name;
state.Apply(message, transaction); state.Apply(message, transaction);
......
...@@ -16,11 +16,11 @@ namespace DotNetCore.CAP ...@@ -16,11 +16,11 @@ namespace DotNetCore.CAP
public IQueueExecutor GetInstance(MessageType messageType) public IQueueExecutor GetInstance(MessageType messageType)
{ {
var queueExectors = _serviceProvider.GetServices<IQueueExecutor>(); var queueExecutors = _serviceProvider.GetServices<IQueueExecutor>();
return messageType == MessageType.Publish return messageType == MessageType.Publish
? queueExectors.FirstOrDefault(x => x is BasePublishQueueExecutor) ? queueExecutors.FirstOrDefault(x => x is BasePublishQueueExecutor)
: queueExectors.FirstOrDefault(x => !(x is BasePublishQueueExecutor)); : queueExecutors.FirstOrDefault(x => !(x is BasePublishQueueExecutor));
} }
} }
} }
\ No newline at end of file
...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.MySql.Test ...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.MySql.Test
}; };
await _storage.StoreReceivedMessageAsync(receivedMessage); await _storage.StoreReceivedMessageAsync(receivedMessage);
var message = await _storage.GetNextReceviedMessageToBeEnqueuedAsync(); var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync();
Assert.NotNull(message); Assert.NotNull(message);
Assert.Equal(StatusName.Scheduled, message.StatusName); Assert.Equal(StatusName.Scheduled, message.StatusName);
......
...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.PostgreSql.Test ...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.PostgreSql.Test
}; };
await _storage.StoreReceivedMessageAsync(receivedMessage); await _storage.StoreReceivedMessageAsync(receivedMessage);
var message = await _storage.GetNextReceviedMessageToBeEnqueuedAsync(); var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync();
Assert.NotNull(message); Assert.NotNull(message);
Assert.Equal(StatusName.Scheduled, message.StatusName); Assert.Equal(StatusName.Scheduled, message.StatusName);
......
...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -121,7 +121,7 @@ namespace DotNetCore.CAP.SqlServer.Test
}; };
await _storage.StoreReceivedMessageAsync(receivedMessage); await _storage.StoreReceivedMessageAsync(receivedMessage);
var message = await _storage.GetNextReceviedMessageToBeEnqueuedAsync(); var message = await _storage.GetNextReceivedMessageToBeEnqueuedAsync();
Assert.NotNull(message); Assert.NotNull(message);
Assert.Equal(StatusName.Scheduled, message.StatusName); Assert.Equal(StatusName.Scheduled, message.StatusName);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment