ARTEMIS-1422 Fix match change to support wildcard config

This commit is contained in:
Michael Andre Pearce 2017-09-15 01:24:09 +01:00 committed by Clebert Suconic
parent 533797fbc4
commit e9eaa7daf6
16 changed files with 212 additions and 73 deletions

View File

@ -392,7 +392,7 @@ public final class XmlDataExporter extends OptionalLocking {
}
};
PagingStoreFactory pageStoreFactory = new PagingStoreFactoryNIO(storageManager, config.getPagingLocation(), 1000L, scheduled, executorFactory, true, null);
HierarchicalRepository<AddressSettings> addressSettingsRepository = new HierarchicalObjectRepository<>();
HierarchicalRepository<AddressSettings> addressSettingsRepository = new HierarchicalObjectRepository<>(config.getWildcardConfiguration());
addressSettingsRepository.setDefault(new AddressSettings());
PagingManager manager = new PagingManagerImpl(pageStoreFactory, addressSettingsRepository);

View File

@ -26,7 +26,7 @@ public class WildcardConfiguration implements Serializable {
static final char DELIMITER = '.';
boolean enabled = true;
boolean routingEnabled = true;
char singleWord = SINGLE_WORD;
@ -34,6 +34,13 @@ public class WildcardConfiguration implements Serializable {
char delimiter = DELIMITER;
String singleWordString = String.valueOf(singleWord);
String anyWordsString = String.valueOf(anyWords);
String delimiterString = String.valueOf(delimiter);
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -41,7 +48,7 @@ public class WildcardConfiguration implements Serializable {
WildcardConfiguration that = (WildcardConfiguration) o;
if (enabled != that.enabled) return false;
if (routingEnabled != that.routingEnabled) return false;
if (singleWord != that.singleWord) return false;
if (anyWords != that.anyWords) return false;
return delimiter == that.delimiter;
@ -50,7 +57,7 @@ public class WildcardConfiguration implements Serializable {
@Override
public int hashCode() {
int result = (enabled ? 1 : 0);
int result = (routingEnabled ? 1 : 0);
result = 31 * result + (int) singleWord;
result = 31 * result + (int) anyWords;
result = 31 * result + (int) delimiter;
@ -60,43 +67,59 @@ public class WildcardConfiguration implements Serializable {
@Override
public String toString() {
return "WildcardConfiguration{" +
"anyWords=" + anyWords +
", enabled=" + enabled +
"routingEnabled=" + routingEnabled +
", anyWords=" + anyWords +
", singleWord=" + singleWord +
", delimiter=" + delimiter +
'}';
}
public boolean isEnabled() {
return enabled;
public boolean isRoutingEnabled() {
return routingEnabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
public void setRoutingEnabled(boolean routingEnabled) {
this.routingEnabled = routingEnabled;
}
public char getAnyWords() {
return anyWords;
}
public String getAnyWordsString() {
return anyWordsString;
}
public void setAnyWords(char anyWords) {
this.anyWords = anyWords;
this.anyWordsString = String.valueOf(anyWords);
}
public char getDelimiter() {
return delimiter;
}
public String getDelimiterString() {
return delimiterString;
}
public void setDelimiter(char delimiter) {
this.delimiter = delimiter;
this.delimiterString = String.valueOf(delimiter);
}
public char getSingleWord() {
return singleWord;
}
public String getSingleWordString() {
return singleWordString;
}
public void setSingleWord(char singleWord) {
this.singleWord = singleWord;
this.singleWordString = String.valueOf(singleWord);
}
public String convert(String filter, WildcardConfiguration to) {

View File

@ -912,14 +912,14 @@ public class ConfigurationImpl implements Configuration, Serializable {
@Override
@Deprecated
public boolean isWildcardRoutingEnabled() {
return wildcardConfiguration.isEnabled();
return wildcardConfiguration.isRoutingEnabled();
}
@Override
@Deprecated
public ConfigurationImpl setWildcardRoutingEnabled(final boolean enabled) {
logger.info("Usage of wildcardRoutingEnabled configuration property is deprecated, please use wildCardConfiguration.enabled instead");
wildcardConfiguration.setEnabled(enabled);
logger.info("Usage of wildcardRoutingEnabled configuration property is deprecated, please use wildCardConfiguration.routingEnabled instead");
wildcardConfiguration.setRoutingEnabled(enabled);
return this;
}

View File

@ -1764,14 +1764,13 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
* @return
*/
protected void parseWildcardConfiguration(final Element e, final Configuration mainConfig) {
WildcardConfiguration conf = new WildcardConfiguration();
WildcardConfiguration conf = mainConfig.getWildcardConfiguration();
conf.setDelimiter(getString(e, "delimiter", Character.toString(conf.getDelimiter()), Validators.NO_CHECK).charAt(0));
conf.setAnyWords(getString(e, "any-words", Character.toString(conf.getAnyWords()), Validators.NO_CHECK).charAt(0));
conf.setSingleWord(getString(e, "single-word", Character.toString(conf.getSingleWord()), Validators.NO_CHECK).charAt(0));
conf.setEnabled(getBoolean(e, "enabled", conf.isEnabled()));
mainConfig.setWildCardConfiguration(conf);
conf.setRoutingEnabled(getBoolean(e, "enabled", conf.isRoutingEnabled()));
conf.setRoutingEnabled(getBoolean(e, "routing-enabled", conf.isRoutingEnabled()));
}
private ConnectorServiceConfiguration parseConnectorService(final Element e) {

View File

@ -157,7 +157,7 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding
this.reaperPriority = reaperPriority;
if (wildcardConfiguration.isEnabled()) {
if (wildcardConfiguration.isRoutingEnabled()) {
addressManager = new WildcardAddressManager(this, wildcardConfiguration, storageManager);
} else {
addressManager = new SimpleAddressManager(this, wildcardConfiguration, storageManager);

View File

@ -423,11 +423,11 @@ public class ActiveMQServerImpl implements ActiveMQServer {
this.securityManager = securityManager;
addressSettingsRepository = new HierarchicalObjectRepository<>();
addressSettingsRepository = new HierarchicalObjectRepository<>(configuration.getWildcardConfiguration());
addressSettingsRepository.setDefault(new AddressSettings());
securityRepository = new HierarchicalObjectRepository<>();
securityRepository = new HierarchicalObjectRepository<>(configuration.getWildcardConfiguration());
securityRepository.setDefault(new HashSet<Role>());

View File

@ -29,7 +29,9 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.HierarchicalRepositoryChangeListener;
@ -43,6 +45,7 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
private static final Logger logger = Logger.getLogger(HierarchicalObjectRepository.class);
private static final WildcardConfiguration DEFAULT_WILDCARD_CONFIGURATION = new WildcardConfiguration();
private boolean listenersEnabled = true;
/**
* The default Match to fall back to
@ -66,7 +69,9 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
/**
* a regex comparator
*/
private final MatchComparator matchComparator = new MatchComparator();
private final MatchComparator matchComparator;
private final WildcardConfiguration wildcardConfiguration;
/**
* a cache
@ -94,6 +99,15 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
*/
private final ArrayList<HierarchicalRepositoryChangeListener> listeners = new ArrayList<>();
public HierarchicalObjectRepository() {
this(null);
}
public HierarchicalObjectRepository(final WildcardConfiguration wildcardConfiguration) {
this.wildcardConfiguration = wildcardConfiguration == null ? DEFAULT_WILDCARD_CONFIGURATION : wildcardConfiguration;
this.matchComparator = new MatchComparator(this.wildcardConfiguration);
}
@Override
public void disableListeners() {
lock.writeLock().lock();
@ -155,9 +169,8 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
if (immutableMatch) {
immutables.add(match);
}
Match.verify(match);
Match<T> match1 = new Match<>(match);
match1.setValue(value);
Match.verify(match, wildcardConfiguration);
Match<T> match1 = new Match<>(match, value, wildcardConfiguration);
matches.put(match, match1);
} finally {
lock.writeLock().unlock();
@ -381,25 +394,35 @@ public class HierarchicalObjectRepository<T> implements HierarchicalRepository<T
private static final long serialVersionUID = -6182535107518999740L;
private final String quotedDelimiter;
private final String anyWords;
private final String singleWord;
MatchComparator(final WildcardConfiguration wildcardConfiguration) {
this.quotedDelimiter = Pattern.quote(wildcardConfiguration.getDelimiterString());
this.singleWord = wildcardConfiguration.getSingleWordString();
this.anyWords = wildcardConfiguration.getAnyWordsString();
}
@Override
public int compare(final String o1, final String o2) {
if (o1.contains(Match.WILDCARD) && !o2.contains(Match.WILDCARD)) {
if (o1.contains(anyWords) && !o2.contains(anyWords)) {
return +1;
} else if (!o1.contains(Match.WILDCARD) && o2.contains(Match.WILDCARD)) {
} else if (!o1.contains(anyWords) && o2.contains(anyWords)) {
return -1;
} else if (o1.contains(Match.WILDCARD) && o2.contains(Match.WILDCARD)) {
} else if (o1.contains(anyWords) && o2.contains(anyWords)) {
return o2.length() - o1.length();
} else if (o1.contains(Match.WORD_WILDCARD) && !o2.contains(Match.WORD_WILDCARD)) {
} else if (o1.contains(singleWord) && !o2.contains(singleWord)) {
return +1;
} else if (!o1.contains(Match.WORD_WILDCARD) && o2.contains(Match.WORD_WILDCARD)) {
} else if (!o1.contains(singleWord) && o2.contains(singleWord)) {
return -1;
} else if (o1.contains(Match.WORD_WILDCARD) && o2.contains(Match.WORD_WILDCARD)) {
String[] leftSplits = o1.split("\\.");
String[] rightSplits = o2.split("\\.");
} else if (o1.contains(singleWord) && o2.contains(singleWord)) {
String[] leftSplits = o1.split(quotedDelimiter);
String[] rightSplits = o2.split(quotedDelimiter);
for (int i = 0; i < leftSplits.length; i++) {
String left = leftSplits[i];
if (left.equals(Match.WORD_WILDCARD)) {
if (rightSplits.length < i || !rightSplits[i].equals(Match.WORD_WILDCARD)) {
if (left.equals(singleWord)) {
if (rightSplits.length < i || !rightSplits[i].equals(singleWord)) {
return -1;
} else {
return +1;

View File

@ -18,6 +18,7 @@ package org.apache.activemq.artemis.core.settings.impl;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
/**
@ -25,68 +26,54 @@ import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
*/
public class Match<T> {
public static final String WORD_WILDCARD = "*";
private static final String WORD_WILDCARD_REPLACEMENT = "[^.]+";
public static final String WILDCARD = "#";
public static final String DOT_WILDCARD = ".#";
private static final String WILDCARD_REPLACEMENT = ".*";
private static final String WILDCARD_CHILD_REPLACEMENT = "(\\..+)*";
private static final String WORD_WILDCARD_REPLACEMENT_FORMAT = "[^%s]+";
private static final String WILDCARD_CHILD_REPLACEMENT_FORMAT = "(%s.+)*";
private static final String DOT = ".";
private static final String DOT_REPLACEMENT = "\\.";
private String match;
private final String match;
private final Pattern pattern;
private T value;
private final T value;
public Match(final String match) {
public Match(final String match, final T value, final WildcardConfiguration wildcardConfiguration) {
this.match = match;
this.value = value;
String actMatch = match;
// replace any regex characters
if (Match.WILDCARD.equals(match)) {
if (wildcardConfiguration.getAnyWordsString().equals(match)) {
// replace any regex characters
actMatch = Match.WILDCARD_REPLACEMENT;
} else {
// this is to match with what's documented
actMatch = actMatch.replace(DOT_WILDCARD, WILDCARD);
actMatch = actMatch.replace(wildcardConfiguration.getDelimiterString() + wildcardConfiguration.getAnyWordsString(), wildcardConfiguration.getAnyWordsString());
actMatch = actMatch.replace(Match.DOT, Match.DOT_REPLACEMENT);
actMatch = actMatch.replace(Match.WORD_WILDCARD, Match.WORD_WILDCARD_REPLACEMENT);
actMatch = actMatch.replace(wildcardConfiguration.getSingleWordString(), String.format(WORD_WILDCARD_REPLACEMENT_FORMAT, Pattern.quote(wildcardConfiguration.getDelimiterString())));
// this one has to be done by last as we are using .* and it could be replaced wrongly
actMatch = actMatch.replace(Match.WILDCARD, Match.WILDCARD_CHILD_REPLACEMENT);
// this one has to be done by last as we are using .* and it could be replaced wrongly if delimiter is '.'
actMatch = actMatch.replace(wildcardConfiguration.getAnyWordsString(), String.format(WILDCARD_CHILD_REPLACEMENT_FORMAT, Pattern.quote(wildcardConfiguration.getDelimiterString())));
}
pattern = Pattern.compile(actMatch);
}
public String getMatch() {
public final String getMatch() {
return match;
}
public void setMatch(final String match) {
this.match = match;
}
public Pattern getPattern() {
public final Pattern getPattern() {
return pattern;
}
public T getValue() {
public final T getValue() {
return value;
}
public void setValue(final T value) {
this.value = value;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
@ -114,11 +101,12 @@ public class Match<T> {
* @param match the match to validate
* @throws IllegalArgumentException if a match isn't valid
*/
public static void verify(final String match) throws IllegalArgumentException {
public static void verify(final String match, final WildcardConfiguration wildcardConfiguration) throws IllegalArgumentException {
if (match == null) {
throw ActiveMQMessageBundle.BUNDLE.nullMatch();
}
if (match.contains("#") && match.indexOf("#") < match.length() - 1) {
final String anyWords = wildcardConfiguration.getAnyWordsString();
if (match.contains(anyWords) && match.indexOf(anyWords) < match.length() - 1) {
throw ActiveMQMessageBundle.BUNDLE.invalidMatch();
}
}

View File

@ -2940,7 +2940,14 @@
<xsd:element maxOccurs="1" minOccurs="0" name="enabled" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
are wildcard addresses enabled
deprecated please use routing-enabled.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="routing-enabled" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
is wildcard addresses routing enabled.
</xsd:documentation>
</xsd:annotation>
</xsd:element>

View File

@ -107,13 +107,13 @@ public class FileConfigurationParserTest extends ActiveMQTestBase {
public void testWildcardConfiguration() throws Exception {
FileConfigurationParser parser = new FileConfigurationParser();
String configStr = firstPart + "<wildcard-addresses>\n<enabled>true</enabled>\n<delimiter>/</delimiter>\n<any-words>></any-words></wildcard-addresses>" + lastPart;
String configStr = firstPart + "<wildcard-addresses>\n<routing-enabled>true</routing-enabled>\n<delimiter>/</delimiter>\n<any-words>></any-words></wildcard-addresses>" + lastPart;
ByteArrayInputStream input = new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8));
Configuration config = parser.parseMainConfig(input);
WildcardConfiguration wildCard = config.getWildcardConfiguration();
assertEquals('/', wildCard.getDelimiter());
assertTrue(wildCard.isEnabled());
assertTrue(wildCard.isRoutingEnabled());
assertEquals('>', wildCard.getAnyWords());
assertEquals('*', wildCard.getSingleWord());
}

View File

@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.artemis.core.config.WildcardConfiguration;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.settings.impl.HierarchicalObjectRepository;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
@ -64,6 +65,44 @@ public class RepositoryTest extends ActiveMQTestBase {
Assert.assertEquals("abd#", repo.getMatch("a.b.d"));
}
@Test
public void testMatchingDocsCustomUnderscorDelimiter() throws Throwable {
WildcardConfiguration wildcardConfiguration = new WildcardConfiguration();
wildcardConfiguration.setDelimiter('_');
HierarchicalObjectRepository<String> repo = new HierarchicalObjectRepository<>(wildcardConfiguration);
repo.addMatch("a_b_#", "ab#");
repo.addMatch("a_b_d_#", "abd#");
repo.addMatch("#", "root");
Assert.assertEquals("ab#", repo.getMatch("a_b"));
Assert.assertEquals("ab#", repo.getMatch("a_b_c"));
Assert.assertEquals("abd#", repo.getMatch("a_b_d_lll"));
Assert.assertEquals("root", repo.getMatch("z_z_z_z_z"));
Assert.assertEquals("root", repo.getMatch("a_babc"));
Assert.assertEquals("ab#", repo.getMatch("a_b_dabc"));
Assert.assertEquals("abd#", repo.getMatch("a_b_d"));
}
@Test
public void testMatchingDocsCustomForwardSlashDelimiter() throws Throwable {
WildcardConfiguration wildcardConfiguration = new WildcardConfiguration();
wildcardConfiguration.setDelimiter('/');
HierarchicalObjectRepository<String> repo = new HierarchicalObjectRepository<>(wildcardConfiguration);
repo.addMatch("a/b/#", "ab#");
repo.addMatch("a/b/d/#", "abd#");
repo.addMatch("#", "root");
Assert.assertEquals("ab#", repo.getMatch("a/b"));
Assert.assertEquals("ab#", repo.getMatch("a/b/c"));
Assert.assertEquals("abd#", repo.getMatch("a/b/d/lll"));
Assert.assertEquals("root", repo.getMatch("z/z/z/z/z"));
Assert.assertEquals("root", repo.getMatch("a/babc"));
Assert.assertEquals("ab#", repo.getMatch("a/b/dabc"));
Assert.assertEquals("abd#", repo.getMatch("a/b/d"));
}
@Test
public void testSingleMatch() {
securityRepository.addMatch("queues.*", new HashSet<Role>());

View File

@ -861,6 +861,14 @@
</xsd:element>
<xsd:element name="addresses" type="addressesType" maxOccurs="1" minOccurs="0" />
<xsd:element name="wildcard-addresses" type="wildcardType" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Wildcard addresses format
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:all>
</xsd:complexType>
@ -2664,4 +2672,48 @@
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="wildcardType">
<xsd:annotation>
<xsd:documentation>
Complex type element to configure wildcard address format.
</xsd:documentation>
</xsd:annotation>
<xsd:all>
<xsd:element maxOccurs="1" minOccurs="0" name="enabled" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
deprecated please use routing-enabled.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="routing-enabled" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation>
is wildcard addresses routing enabled.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="delimiter" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
wildcard address parts delimiter. Default '.'
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="any-words" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
wildcard address any words character. Default '#'
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="single-word" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
wildcard address single word character. Default '*'
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:all>
</xsd:complexType>
</xsd:schema>

View File

@ -15,7 +15,7 @@ messages which are sent to a *hierarchy* of addresses.
This functionality is enabled by default. To turn it off add the following to the `broker.xml` configuration.
<wildcard-addresses>
<enabled>false</enabled>
<routing-enabled>false</routing-enabled>
</wildcard-addresses>
For more information on the wild card syntax and how to configure it, take a look at [wildcard syntax](wildcard-syntax.md) chapter,

View File

@ -32,7 +32,7 @@ It's possible to further configure the syntax of the wildcard addresses using th
For that, the `<wildcard-addresses>` configuration tag is used.
<wildcard-addresses>
<enabled>true</enabled>
<routing-enabled>true</routing-enabled>
<delimiter>.</delimiter>
<any-words>#</any-words>
<single-word>*</single-word>

View File

@ -112,6 +112,10 @@ under the License.
</address-setting>
</address-settings>
<wildcard-addresses>
<delimiter>_</delimiter>
</wildcard-addresses>
<addresses>
<address name="config_test_queue_removal">
<multicast>

View File

@ -115,6 +115,10 @@ under the License.
</address-setting>
</address-settings>
<wildcard-addresses>
<delimiter>_</delimiter>
</wildcard-addresses>
<addresses>
<address name="config_test_queue_removal">
<multicast>