
com.networknt.tram.command.consumer.CommandDispatcher Maven / Gradle / Ivy
package com.networknt.tram.command.consumer;
import com.networknt.config.JsonMapper;
import com.networknt.tram.command.common.ChannelMapping;
import com.networknt.tram.command.common.CommandMessageHeaders;
import com.networknt.tram.command.common.Failure;
import com.networknt.tram.command.common.ReplyMessageHeaders;
import com.networknt.tram.command.common.paths.ResourcePath;
import com.networknt.tram.command.common.paths.ResourcePathPattern;
import com.networknt.tram.message.common.Message;
import com.networknt.tram.message.consumer.MessageConsumer;
import com.networknt.tram.message.producer.MessageBuilder;
import com.networknt.tram.message.producer.MessageProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static java.util.Collections.EMPTY_MAP;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toSet;
public class CommandDispatcher {
private Logger logger = LoggerFactory.getLogger(getClass());
private String commandDispatcherId;
private CommandHandlers commandHandlers;
private ChannelMapping channelMapping;
private MessageConsumer messageConsumer;
private MessageProducer messageProducer;
public CommandDispatcher(String commandDispatcherId,
CommandHandlers commandHandlers,
ChannelMapping channelMapping,
MessageConsumer messageConsumer,
MessageProducer messageProducer) {
this.commandDispatcherId = commandDispatcherId;
this.commandHandlers = commandHandlers;
this.channelMapping = channelMapping;
this.messageConsumer = messageConsumer;
this.messageProducer = messageProducer;
initialize();
}
public void initialize() {
messageConsumer.subscribe(commandDispatcherId,
commandHandlers.getChannels().stream().map(channelMapping::transform).collect(toSet()),
this::messageHandler);
}
public void messageHandler(Message message) {
logger.trace("Received message {} {}", commandDispatcherId, message);
Optional possibleMethod = commandHandlers.findTargetMethod(message);
if (!possibleMethod.isPresent()) {
throw new RuntimeException("No method for " + message);
}
CommandHandler m = possibleMethod.get();
Object param = convertPayload(m, message.getPayload());
Map correlationHeaders = correlationHeaders(message.getHeaders());
Map pathVars = getPathVars(message, m);
Optional defaultReplyChannel = message.getHeader(CommandMessageHeaders.REPLY_TO);
List replies;
try {
CommandMessage cm = new CommandMessage(message.getId(), param, correlationHeaders, message);
replies = invoke(m, cm, pathVars);
logger.trace("Generated replies {} {} {}", commandDispatcherId, message, replies);
} catch (Exception e) {
logger.trace("Generated error {} {} {}", commandDispatcherId, message, e.getClass().getName());
handleException(message, param, m, e, pathVars, defaultReplyChannel);
return;
}
if (replies != null) {
publish(correlationHeaders, replies, defaultReplyChannel);
} else {
logger.trace("Null replies - not publishling");
}
}
protected List invoke(CommandHandler commandHandler, CommandMessage cm, Map pathVars) {
return commandHandler.invokeMethod(cm, pathVars);
}
protected Object convertPayload(CommandHandler m, String payload) {
Class> paramType = findCommandParameterType(m);
return JsonMapper.fromJson(payload, paramType);
}
private Map getPathVars(Message message, CommandHandler handler) {
return handler.getResource().flatMap( res -> {
ResourcePathPattern r = ResourcePathPattern.parse(res);
return message.getHeader(CommandMessageHeaders.RESOURCE).map(h -> {
ResourcePath mr = ResourcePath.parse(h);
return r.getPathVariableValues(mr);
});
}).orElse(EMPTY_MAP);
}
private void publish(Map correlationHeaders, List replies, Optional defaultReplyChannel) {
for (Message reply : replies)
messageProducer.send(channelMapping.transform(destination(defaultReplyChannel)),
MessageBuilder
.withMessage(reply)
.withExtraHeaders("", correlationHeaders)
.build());
}
private String destination(Optional defaultReplyChannel) {
return defaultReplyChannel.orElseGet(() -> {
throw new RuntimeException();
});
}
private Map correlationHeaders(Map headers) {
Map m = headers.entrySet()
.stream()
.filter(e -> e.getKey().startsWith(CommandMessageHeaders.COMMAND_HEADER_PREFIX))
.collect(Collectors.toMap(e -> CommandMessageHeaders.inReply(e.getKey()),
Map.Entry::getValue));
m.put(ReplyMessageHeaders.IN_REPLY_TO, headers.get(Message.ID));
return m;
}
private void handleException(Message message, Object param,
CommandHandler commandHandler,
Throwable cause,
Map pathVars,
Optional defaultReplyChannel) {
Optional m = commandHandlers.findExceptionHandler(cause);
logger.info("Handler for {} is {}", cause.getClass(), m);
if (m.isPresent()) {
List replies = m.get().invoke(cause);
publish(correlationHeaders(message.getHeaders()), replies, defaultReplyChannel);
} else {
List replies = singletonList(MessageBuilder.withPayload(JsonMapper.toJson(new Failure())).build());
publish(correlationHeaders(message.getHeaders()), replies, defaultReplyChannel);
}
}
private Class findCommandParameterType(CommandHandler m) {
return m.getCommandClass();
}
public void finish() {
messageConsumer.close();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy