/// <inheritdoc /> protected override async Task <IEnumerable <QueuedMessage> > GetPendingMessages(CancellationToken cancellationToken = new CancellationToken()) { var pendingMessages = new List <QueuedMessage>(); var files = _directory.EnumerateFiles("*.pmsg"); foreach (var file in files) { try { var messageFile = new MessageFile(file); var message = await messageFile.ReadMessage(cancellationToken); if (message.IsEncrypted() && _messageEncryptionService != null) { message = await _messageEncryptionService.Decrypt(message); } var principal = await _securityTokenService.NullSafeValidate(message.Headers.SecurityToken); var queuedMessage = new QueuedMessage(message, principal); pendingMessages.Add(queuedMessage); } catch (Exception ex) { DiagnosticService.Emit(new FilesystemEventBuilder(this, FilesystemEventType.MessageFileFormatError) { Detail = "Error reading previously queued message file; skipping", Path = file.FullName, Exception = ex }.Build()); } } return(pendingMessages); }
public async Task Given_ClaimsPrincipal_When_Reading_Principal_Should_Be_Read() { var tempDir = GetTempDirectory(); var queueName = new QueueName(Guid.NewGuid().ToString()); var queuePath = Path.Combine(tempDir.FullName, queueName); var queueDir = new DirectoryInfo(queuePath); if (!queueDir.Exists) { queueDir.Create(); } var message = new Message(new MessageHeaders { {HeaderName.ContentType, "text/plain"}, {HeaderName.MessageId, Guid.NewGuid().ToString()} }, "Hello, world!"); var senderPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("username", "testuser"), new Claim("role", "testrole") })); var file = (await MessageFile.Create(queueDir, message, senderPrincipal)).File; var messageFile = new MessageFile(file); var readSenderPrincipal = await messageFile.ReadSenderPrincipal(); var readMessage = await messageFile.ReadMessage(); Assert.That(readSenderPrincipal, Is.EqualTo(senderPrincipal).Using(new ClaimsPrincipalEqualityComparer())); Assert.That(readMessage, Is.EqualTo(message).Using(new MessageEqualityComparer())); }
private async Task MoveToDeadLetterDirectory(QueuedMessage queuedMessage) { var message = queuedMessage.Message; var headers = message.Headers; var pattern = headers.MessageId + "*.pmsg"; var matchingFiles = _directory.EnumerateFiles(pattern); foreach (var matchingFile in matchingFiles) { var messageFile = new MessageFile(matchingFile); var deadLetter = await messageFile.MoveTo(_deadLetterDirectory); await DiagnosticService.EmitAsync( new FilesystemEventBuilder(this, DiagnosticEventType.DeadLetter) { Detail = "Message file deleted", Message = message, Queue = QueueName, Path = deadLetter.File.FullName }.Build()); } }
private async Task CreateMessageFile(QueuedMessage queuedMessage) { var message = queuedMessage.Message; var principal = queuedMessage.Principal; var securityToken = await _securityTokenService.NullSafeIssue(principal, message.Headers.Expires); var storedMessage = message.WithSecurityToken(securityToken); if (_messageEncryptionService != null) { storedMessage = await _messageEncryptionService.Encrypt(storedMessage); } var messageFile = await MessageFile.Create(_directory, storedMessage); await DiagnosticService.EmitAsync( new FilesystemEventBuilder(this, FilesystemEventType.MessageFileCreated) { Detail = "Message file created", Message = message, Queue = QueueName, Path = messageFile.File.FullName }.Build()); }
public FilesystemQueuedMessageContext(MessageFile queuedMessage) { _queuedMessage = queuedMessage; }
// ReSharper disable once UnusedMethodReturnValue.Local private async Task ProcessQueuedMessage(MessageFile queuedMessage, CancellationToken cancellationToken) { var attemptCount = 0; var deadLetter = false; // ReSharper disable once ConditionIsAlwaysTrueOrFalse while (!deadLetter && attemptCount < _maxAttempts) { attemptCount++; Log.DebugFormat("Processing queued message {0} (attempt {1} of {2})...", queuedMessage.File, attemptCount, _maxAttempts); var context = new FilesystemQueuedMessageContext(queuedMessage); cancellationToken.ThrowIfCancellationRequested(); await _concurrentMessageProcessingSlot.WaitAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); try { var message = await queuedMessage.ReadMessage(cancellationToken); await _listener.MessageReceived(message, context, cancellationToken); if (_autoAcknowledge && !context.Acknowledged) { await context.Acknowledge(); } } catch (MessageFileFormatException ex) { Log.ErrorFormat("Unable to read invalid or corrupt message file {0}", ex, ex.Path); deadLetter = true; } catch (Exception ex) { Log.WarnFormat("Unhandled exception handling queued message file {0}", ex, queuedMessage.File); } finally { _concurrentMessageProcessingSlot.Release(); } if (context.Acknowledged) { Log.DebugFormat("Message acknowledged. Deleting message file {0}...", queuedMessage.File); // TODO: Implement journaling await queuedMessage.Delete(cancellationToken); Log.DebugFormat("Message file {0} deleted successfully", queuedMessage.File); return; } // TODO: Use TTL/Expiry instead of max attempts per queue if (attemptCount >= _maxAttempts) { Log.WarnFormat("Maximum attempts to proces message file {0} exceeded", queuedMessage.File); deadLetter = true; } if (deadLetter) { await queuedMessage.MoveTo(_deadLetterDirectory, cancellationToken); return; } Log.DebugFormat("Message not acknowledged. Retrying in {0}...", _retryDelay); await Task.Delay(_retryDelay, cancellationToken); } }
private async Task EnqueueExistingFiles(CancellationToken cancellationToken) { var files = _directory.EnumerateFiles(); foreach (var file in files) { Log.DebugFormat("Enqueueing existing message from file {0}...", file); var queuedMessage = new MessageFile(file); await _queuedMessages.SendAsync(queuedMessage, cancellationToken); } }