io.axonif.queuebacca.consumers.ScopedMessageConsumer Maven / Gradle / Ivy
/*
* Copyright 2019 The Queuebacca Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.axonif.queuebacca.consumers;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.stream.Collectors;
import io.axonif.queuebacca.Message;
import io.axonif.queuebacca.MessageConsumer;
import io.axonif.queuebacca.MessageContext;
import io.axonif.queuebacca.MessageResponse;
/**
* A {@link MessageConsumer} that allows for additional, processing of {@link Message Messages} using a chain
* of responsibility.
*
* @param the message type
*/
public final class ScopedMessageConsumer implements MessageConsumer {
/**
* A processor to provide additional processing before or after a {@link Message} is consumed.
*/
public interface MessageScope {
/**
* Perform processing using the provided {@link M message} and {@link MessageContext}. In order to continue the chain,
* a call to {@link MessageScopeChain#next()} is required. Failing to do so will result in
* the message being disposed of and considered successfully consumed, which may be the desired result.
*
* @param the message type
* @param message the message being consumed
* @param messageContext the context of the message
* @param messageScopeChain the chain to continue processing to consumption
* @return a {@link MessageResponse} indicating how to handle the {@link Message} after it's been consumed
*/
MessageResponse wrap(M message, MessageContext messageContext, MessageScopeChain messageScopeChain);
}
/**
* The {@link MessageScope} chain used to continue invoking processors until the final call to the {@link MessageConsumer}.
*/
public interface MessageScopeChain {
/**
* Calls the next {@link MessageScope}, or if there are none left, the {@link MessageConsumer}.
*/
MessageResponse next();
}
private final MessageConsumer consumer;
private final List messageScopes;
/**
* Creates a new instance of a {@link ScopedMessageConsumer} with a {@link MessageConsumer} it will delegate to,
* and one or more {@link MessageScope MessageScopes}.
*
* @param consumer the consumer being decorated
* @param messageScope the first processor
* @param messageScopes any additional processors
*/
public ScopedMessageConsumer(MessageConsumer consumer, MessageScope messageScope, MessageScope... messageScopes) {
requireNonNull(messageScope);
requireNonNull(messageScopes);
this.consumer = requireNonNull(consumer);
this.messageScopes = concat(of(messageScope), of(messageScopes)).collect(Collectors.toList());
}
/**
* {@inheritDoc}
*
* This consumer will run the {@link Message} through a chain of {@link MessageScope MessageScopes} before
* invoking it's {@link MessageConsumer}.
*
* @return
*/
@Override
public MessageResponse consume(M message, MessageContext messageContext) {
requireNonNull(message);
requireNonNull(messageContext);
ConcreteMessageScopeChain messageScopeChain = new ConcreteMessageScopeChain(new LinkedList<>(messageScopes), message, messageContext);
return messageScopeChain.next();
}
/**
* The {@link MessageScopeChain} implementation containing a queue of {@link MessageScope MessageScopes}.
*/
private final class ConcreteMessageScopeChain implements MessageScopeChain {
private final Queue messageScopes;
private final M message;
private final MessageContext messageContext;
ConcreteMessageScopeChain(Queue messageScopes, M message, MessageContext messageContext) {
this.messageScopes = messageScopes;
this.message = message;
this.messageContext = messageContext;
}
@Override
public MessageResponse next() {
requireNonNull(message);
requireNonNull(messageContext);
if (!messageScopes.isEmpty()) {
MessageScope messageScope = messageScopes.poll();
return messageScope.wrap(message, messageContext, this);
} else {
return consumer.consume(message, messageContext);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy