INT-1969 adding type-difference-weight algorithm for more nuanced decision making with regard to class and/or interface hierarchies for the PayloadTypeRouter
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -22,9 +22,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A Message Router that resolves the {@link MessageChannel} based on the
|
||||
@@ -35,6 +33,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class PayloadTypeRouter extends AbstractMessageRouter {
|
||||
|
||||
private static final String ARRAY_SUFFIX = "[]";
|
||||
|
||||
/**
|
||||
* Selects the most appropriate channel name matching channel identifiers which are the
|
||||
* fully qualified class names encountered while traversing the payload type hierarchy.
|
||||
@@ -51,68 +51,81 @@ public class PayloadTypeRouter extends AbstractMessageRouter {
|
||||
}
|
||||
|
||||
private String getChannelName(Message<?> message) {
|
||||
Class<?> type = message.getPayload().getClass();
|
||||
boolean array = type.isArray();
|
||||
if (array){
|
||||
type = type.getComponentType();
|
||||
}
|
||||
while (type != null && !CollectionUtils.isEmpty(this.channelIdentifierMap)) {
|
||||
|
||||
String mappingName = null;
|
||||
if (array){
|
||||
mappingName = type.getName()+"[]";
|
||||
}
|
||||
else {
|
||||
mappingName = type.getName();
|
||||
}
|
||||
String channelName = this.channelIdentifierMap.get(mappingName);
|
||||
if (StringUtils.hasText(channelName)) {
|
||||
return channelName;
|
||||
}
|
||||
// next, check for interfaces (super inluded) of this type
|
||||
|
||||
channelName = this.introspectInterfaces(type, message);
|
||||
|
||||
if (channelName != null) {
|
||||
return channelName;
|
||||
}
|
||||
|
||||
// finally, continue up the hierarchy
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String introspectInterfaces(Class<?> type, Message<?> message){
|
||||
Class<?>[] interfaces = type.getInterfaces();
|
||||
List<String> matchedInterfaces = new ArrayList<String>();
|
||||
|
||||
this.doIntrospect(interfaces, matchedInterfaces);
|
||||
|
||||
if (matchedInterfaces.isEmpty()){
|
||||
if (CollectionUtils.isEmpty(this.channelIdentifierMap)) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if (matchedInterfaces.size() > 1){
|
||||
throw new MessageHandlingException(message,
|
||||
"Unresolvable ambiguity while attempting to find closest match for [" + type.getName() +
|
||||
"]. Candidate types " + matchedInterfaces + " have equal weight.");
|
||||
}
|
||||
else {
|
||||
return matchedInterfaces.get(0);
|
||||
}
|
||||
Class<?> type = message.getPayload().getClass();
|
||||
boolean isArray = type.isArray();
|
||||
if (isArray) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
return this.findClosestMatch(type, isArray);
|
||||
}
|
||||
|
||||
private void doIntrospect(Class<?>[] interfaces, List<String> matchedInterfaces){
|
||||
for (Class<?> extendedInterface : interfaces) {
|
||||
String currentChannelName = this.channelIdentifierMap.get(extendedInterface.getName());
|
||||
if (StringUtils.hasText(currentChannelName)) {
|
||||
matchedInterfaces.add(extendedInterface.getName());
|
||||
|
||||
private String findClosestMatch(Class<?> type, boolean isArray) {
|
||||
int minTypeDiffWeight = Integer.MAX_VALUE;
|
||||
String closestMatch = null;
|
||||
boolean ambiguityAtClosestMatchLevel = false;
|
||||
for (String candidate : this.channelIdentifierMap.keySet()) {
|
||||
if (isArray) {
|
||||
if (!candidate.endsWith(ARRAY_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
// trim the suffix
|
||||
candidate = candidate.substring(0, candidate.length() - ARRAY_SUFFIX.length());
|
||||
}
|
||||
else if (candidate.endsWith(ARRAY_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
int typeDiffWeight = determineTypeDifferenceWeight(candidate, type, 0);
|
||||
if (typeDiffWeight < minTypeDiffWeight) {
|
||||
minTypeDiffWeight = typeDiffWeight;
|
||||
closestMatch = (isArray) ? candidate + ARRAY_SUFFIX : candidate;
|
||||
ambiguityAtClosestMatchLevel = false;
|
||||
}
|
||||
else if (typeDiffWeight == minTypeDiffWeight && typeDiffWeight != Integer.MAX_VALUE) {
|
||||
ambiguityAtClosestMatchLevel = true;
|
||||
}
|
||||
Class<?>[] extendedInterfaces = extendedInterface.getInterfaces();
|
||||
this.doIntrospect(extendedInterfaces, matchedInterfaces);
|
||||
}
|
||||
if (ambiguityAtClosestMatchLevel) {
|
||||
throw new IllegalStateException(
|
||||
"Unresolvable ambiguity while attempting to find closest match for [" + type.getName() + "].");
|
||||
}
|
||||
if (closestMatch == null) {
|
||||
return null;
|
||||
}
|
||||
return this.channelIdentifierMap.get(closestMatch);
|
||||
}
|
||||
|
||||
private int determineTypeDifferenceWeight(String candidate, Class<?> type, int level) {
|
||||
if (type.getName().equals(candidate)) {
|
||||
return level;
|
||||
}
|
||||
List<String> matchingIntefaces = new ArrayList<String>();
|
||||
for (Class<?> iface : type.getInterfaces()) {
|
||||
if (iface.getName().equals(candidate)) {
|
||||
matchingIntefaces.add(candidate);
|
||||
}
|
||||
if (matchingIntefaces.size() > 1) {
|
||||
throw new IllegalStateException("Found more than one matching interface at the same level of " +
|
||||
"the hierarchy while attempting to find closest match for [" + type.getName() + "].");
|
||||
}
|
||||
if (matchingIntefaces.size() == 1) {
|
||||
return level + 1;
|
||||
}
|
||||
// check interface hierarchy
|
||||
for (Class<?> superInterface : iface.getInterfaces()) {
|
||||
int weight = this.determineTypeDifferenceWeight(candidate, superInterface, level + 2);
|
||||
if (weight < Integer.MAX_VALUE) {
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type.getSuperclass() == null) {
|
||||
// exhausted hierarchy and found no match
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return this.determineTypeDifferenceWeight(candidate, type.getSuperclass(), level + 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user