/*
* Copyright 2011 the original author or 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.vertx.ext.amqp.impl.routing;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.amqp.MessagingException;
import io.vertx.ext.amqp.impl.AmqpServiceConfig;
import io.vertx.ext.amqp.impl.config.AmqpServiceConfigImpl;
import io.vertx.ext.amqp.impl.config.ConfigRouteEntry;
import io.vertx.ext.amqp.impl.protocol.InboundMessage;
import io.vertx.ext.amqp.impl.protocol.LinkManager;
import io.vertx.ext.amqp.impl.util.LogManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static io.vertx.ext.amqp.impl.util.Functions.format;
/**
* Contains the routing logic used by the AMQP Service
*
* @author <a href="mailto:rajith@redhat.com">Rajith Attapattu</a>
*/
public class MessageRouter {
private static final LogManager LOG = LogManager.get("MessageRouter:", LinkManager.class);
private final AmqpServiceConfig _config;
public MessageRouter(AmqpServiceConfig config) {
_config = config;
StringBuilder b = new StringBuilder();
b.append("Router Config \n[\n");
b.append("Default outbound-AMQP-address : ").append(config.getDefaultOutboundAddress()).append("\n");
b.append("Default inbound-vertx-address : ").append(config.getDefaultInboundAddress()).append("\n");
b.append("Default vertx handler address : ").append(config.getDefaultHandlerAddress()).append("\n");
b.append("Additional handler address list : ").append(config.getHandlerAddressList()).append("\n");
b.append("]\n");
LOG.info(b.toString());
}
public List<String> routeOutgoing(Message<JsonObject> vertxMsg) throws MessagingException {
String routingKey = extractOutgoingRoutingKey(vertxMsg);
List<String> addrList = new ArrayList<String>();
System.out.println("outgoing route map " + _config.getInboundRoutes());
for (String key : _config.getOutboundRoutes().keySet()) {
ConfigRouteEntry route = _config.getOutboundRoutes().get(key);
if (route.getPattern().matcher(routingKey).matches()) {
addrList.addAll(route.getAddressList());
}
}
if (addrList.size() == 0) {
addrList.add(_config.getDefaultOutboundAddress());
}
return addrList;
}
/**
* By default it uses the address field.
* <p/>
* If a custom routing-property is specified in config it will look to see,
* if that property is available <br>
* 1. As field within the Json message <br>
* 2. If a "properties" field is available, it will look for it as a sub
* field under that <br>
* 3. If an "application-properties" field is available, it will look for it
* as a sub field under that
* <p/>
* If no custome property is specified, then it looks if "vertx.routing-key"
* is specified as a field within the Json message.
*/
private String extractOutgoingRoutingKey(Message<JsonObject> vertxMsg) {
String routingKey = null;
if (vertxMsg.body().containsKey("vertx.routing-key")) {
routingKey = vertxMsg.body().getString("vertx.routing-key");
} else if (_config.isUseCustomPropertyForOutbound() && _config.getOutboundRoutingPropertyName() != null) {
if (vertxMsg.body().containsKey(_config.getOutboundRoutingPropertyName())) {
routingKey = vertxMsg.body().getString(_config.getOutboundRoutingPropertyName());
} else if (vertxMsg.body().containsKey("properties")
&& vertxMsg.body().getJsonObject("properties") instanceof Map
&& vertxMsg.body().getJsonObject("properties")
.containsKey(_config.getOutboundRoutingPropertyName())) {
routingKey = vertxMsg.body().getJsonObject("properties")
.getString(_config.getOutboundRoutingPropertyName());
} else if (vertxMsg.body().containsKey("application-properties")
&& vertxMsg.body().getJsonObject("application-properties") instanceof Map
&& vertxMsg.body().getJsonObject("application-properties")
.containsKey(_config.getOutboundRoutingPropertyName())) {
routingKey = vertxMsg.body().getJsonObject("application-properties")
.getString(_config.getOutboundRoutingPropertyName());
}
if (LOG.isDebugEnabled()) {
LOG.debug("\n============= Custom Routing Property ============");
LOG.debug("Custom routing property name : %s", _config.getOutboundRoutingPropertyName());
LOG.debug("Routing property value : %s", routingKey);
LOG.debug("============= /Custom Routing Property ============/n");
}
}
if (routingKey == null) {
routingKey = vertxMsg.address();
}
return routingKey;
}
public List<String> routeIncoming(InboundMessage amqpMsg, String alternateKey) {
String routingKey = extractIncomingRoutingKey(amqpMsg);
LOG.info(format("Inbound routing info [key=%s, value=%s]", _config.getInboundRoutingPropertyType(),
routingKey));
if (routingKey == null || routingKey.trim().isEmpty()) {
routingKey = alternateKey;
}
List<String> addressList = new ArrayList<String>();
System.out.println("inbound route map " + _config.getInboundRoutes());
for (String k : _config.getInboundRoutes().keySet()) {
ConfigRouteEntry route = _config.getInboundRoutes().get(k);
if (route.getPattern().matcher(routingKey).matches()) {
addressList.addAll(route.getAddressList());
}
}
// no matches
if (addressList.size() == 0) {
// use default if specified.
if (_config.getDefaultInboundAddress() != null) {
addressList.add(_config.getDefaultInboundAddress());
} else {
// else use the routing key as the vertx address
addressList.add(routingKey);
}
}
return addressList;
}
private String extractIncomingRoutingKey(InboundMessage amqpMsg) {
switch (_config.getInboundRoutingPropertyType()) {
case ADDRESS:
return amqpMsg.getAddress();
case SUBJECT:
return amqpMsg.getSubject();
case CUSTOM:
return (String) amqpMsg.getApplicationProperties().get(_config.getInboundRoutingPropertyName());
default:
return null;
}
}
public void addOutboundRoute(String eventbusAddressPattern, String amqpAddress) {
if (_config.getOutboundRoutes().containsKey(eventbusAddressPattern)) {
_config.getOutboundRoutes().get(eventbusAddressPattern).add(amqpAddress);
} else {
_config.getOutboundRoutes().put(eventbusAddressPattern,
AmqpServiceConfigImpl.createRouteEntry(eventbusAddressPattern, amqpAddress));
}
if (LOG.isInfoEnabled()) {
LOG.info("\n============= Outbound Route ============");
LOG.info("Adding the route entry : {%s : %s}", eventbusAddressPattern, amqpAddress);
LOG.info("============= /Outbound Route) ============\n");
}
}
public void removeOutboundRoute(String eventbusAddressPattern, String amqpAddress) {
if (_config.getOutboundRoutes().containsKey(eventbusAddressPattern)) {
ConfigRouteEntry entry = _config.getOutboundRoutes().get(eventbusAddressPattern);
entry.remove(amqpAddress);
if (entry.getAddressList().size() == 0) {
_config.getOutboundRoutes().remove(eventbusAddressPattern);
}
if (LOG.isInfoEnabled()) {
LOG.info("\n============= Outbound Route ============");
LOG.info("Removing the route entry : {%s : %s}", eventbusAddressPattern, amqpAddress);
LOG.info("============= /Outbound Route) ============\n");
}
}
}
public void addInboundRoute(String amqpAddressPattern, String eventbusAddress) {
if (_config.getInboundRoutes().containsKey(amqpAddressPattern)) {
_config.getInboundRoutes().get(amqpAddressPattern).add(eventbusAddress);
} else {
_config.getInboundRoutes().put(amqpAddressPattern,
AmqpServiceConfigImpl.createRouteEntry(amqpAddressPattern, eventbusAddress));
}
if (LOG.isInfoEnabled()) {
LOG.info("\n============= Inbound Route ============");
LOG.info("Adding the route entry : {%s : %s}", amqpAddressPattern, eventbusAddress);
LOG.info("============= /Inbound Route) ============\n");
}
}
public void removeInboundRoute(String amqpAddressPattern, String eventbusAddress) {
if (_config.getOutboundRoutes().containsKey(amqpAddressPattern)) {
ConfigRouteEntry entry = _config.getOutboundRoutes().get(amqpAddressPattern);
entry.remove(eventbusAddress);
if (entry.getAddressList().size() == 0) {
_config.getOutboundRoutes().remove(amqpAddressPattern);
}
if (LOG.isInfoEnabled()) {
LOG.info("\n============= Inbound Route ============");
LOG.info(String.format("Removing the route entry : {%s : %s}", amqpAddressPattern, eventbusAddress));
LOG.info("============= /Inbound Route) ============\n");
}
}
}
}