INT-3936: GlobalChIntercep: add negative pattern
JIRA: https://jira.spring.io/browse/INT-3936 * Extract `smartMatch()` logic from the `IntegrationManagementConfigurer` and `IntegrationMBeanExporter` into the `PatternMatchUtils` class * Add negative (`!`) pattern matching configuration support to the `GlobalChannelInterceptor` annotation and `<int:channel-interceptor>` component * Code style, Docs and JavaDocs polishing
This commit is contained in:
committed by
Artem Bilan
parent
671097fd50
commit
40783ff547
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014 the original author or authors.
|
||||
* Copyright 2014-2017 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.
|
||||
@@ -27,12 +27,15 @@ import java.lang.annotation.Target;
|
||||
* annotation will be applied as global channel interceptors
|
||||
* using the provided {@code patterns} to match channel names.
|
||||
* <p>
|
||||
* This annotation can be used at the {@code class} level for {@link org.springframework.stereotype.Component} beans
|
||||
* This annotation can be used at the {@code class} level
|
||||
* for {@link org.springframework.stereotype.Component} beans
|
||||
* and on methods with {@link org.springframework.context.annotation.Bean}.
|
||||
* <p>
|
||||
* This annotation is an analogue of {@code <int:channel-interceptor/>}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @author Meherzad Lahewala
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@@ -41,9 +44,12 @@ import java.lang.annotation.Target;
|
||||
public @interface GlobalChannelInterceptor {
|
||||
|
||||
/**
|
||||
* An array of simple patterns against which channel names will be matched. Default is "*"
|
||||
* (all channels). See {@link org.springframework.util.PatternMatchUtils#simpleMatch(String, String)}.
|
||||
* An array of patterns against which channel names will be matched.
|
||||
* Since version 5.0 negative patterns are also supported.
|
||||
* A leading '!' negates the pattern match.
|
||||
* Default is "*" (all channels).
|
||||
* @return The pattern.
|
||||
* @see org.springframework.integration.util.PatternMatchUtils#smartMatch(String, String...)
|
||||
*/
|
||||
String[] patterns() default "*";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -37,10 +37,10 @@ import org.springframework.core.OrderComparator;
|
||||
import org.springframework.integration.channel.ChannelInterceptorAware;
|
||||
import org.springframework.integration.channel.interceptor.GlobalChannelInterceptorWrapper;
|
||||
import org.springframework.integration.channel.interceptor.VetoCapableInterceptor;
|
||||
import org.springframework.integration.util.PatternMatchUtils;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -50,6 +50,8 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @author Meherzad Lahewala
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, SmartInitializingSingleton {
|
||||
@@ -59,11 +61,9 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart
|
||||
|
||||
private final OrderComparator comparator = new OrderComparator();
|
||||
|
||||
private final Set<GlobalChannelInterceptorWrapper> positiveOrderInterceptors =
|
||||
new LinkedHashSet<GlobalChannelInterceptorWrapper>();
|
||||
private final Set<GlobalChannelInterceptorWrapper> positiveOrderInterceptors = new LinkedHashSet<>();
|
||||
|
||||
private final Set<GlobalChannelInterceptorWrapper> negativeOrderInterceptors =
|
||||
new LinkedHashSet<GlobalChannelInterceptorWrapper>();
|
||||
private final Set<GlobalChannelInterceptorWrapper> negativeOrderInterceptors = new LinkedHashSet<>();
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
@@ -89,6 +89,7 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart
|
||||
this.negativeOrderInterceptors.add(channelInterceptor);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, ChannelInterceptorAware> channels =
|
||||
this.beanFactory.getBeansOfType(ChannelInterceptorAware.class);
|
||||
for (Entry<String, ChannelInterceptorAware> entry : channels.entrySet()) {
|
||||
@@ -104,15 +105,18 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Applying global interceptors on channel '" + beanName + "'");
|
||||
}
|
||||
List<GlobalChannelInterceptorWrapper> tempInterceptors = new ArrayList<GlobalChannelInterceptorWrapper>();
|
||||
|
||||
List<GlobalChannelInterceptorWrapper> tempInterceptors = new ArrayList<>();
|
||||
for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : this.positiveOrderInterceptors) {
|
||||
String[] patterns = globalChannelInterceptorWrapper.getPatterns();
|
||||
patterns = StringUtils.trimArrayElements(patterns);
|
||||
if (PatternMatchUtils.simpleMatch(patterns, beanName)) {
|
||||
if (beanName != null && Boolean.TRUE.equals(PatternMatchUtils.smartMatch(beanName, patterns))) {
|
||||
tempInterceptors.add(globalChannelInterceptorWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(tempInterceptors, this.comparator);
|
||||
|
||||
for (GlobalChannelInterceptorWrapper next : tempInterceptors) {
|
||||
ChannelInterceptor channelInterceptor = next.getChannelInterceptor();
|
||||
if (!(channelInterceptor instanceof VetoCapableInterceptor)
|
||||
@@ -122,14 +126,17 @@ final class GlobalChannelInterceptorProcessor implements BeanFactoryAware, Smart
|
||||
}
|
||||
|
||||
tempInterceptors.clear();
|
||||
|
||||
for (GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper : this.negativeOrderInterceptors) {
|
||||
String[] patterns = globalChannelInterceptorWrapper.getPatterns();
|
||||
patterns = StringUtils.trimArrayElements(patterns);
|
||||
if (PatternMatchUtils.simpleMatch(patterns, beanName)) {
|
||||
if (beanName != null && Boolean.TRUE.equals(PatternMatchUtils.smartMatch(beanName, patterns))) {
|
||||
tempInterceptors.add(globalChannelInterceptorWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(tempInterceptors, this.comparator);
|
||||
|
||||
if (!tempInterceptors.isEmpty()) {
|
||||
for (int i = tempInterceptors.size() - 1; i >= 0; i--) {
|
||||
ChannelInterceptor channelInterceptor = tempInterceptors.get(i).getChannelInterceptor();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2017 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.
|
||||
@@ -30,8 +30,8 @@ import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.integration.support.management.IntegrationManagement.ManagementOverrides;
|
||||
import org.springframework.integration.util.PatternMatchUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @author Meherzad Lahewala
|
||||
*
|
||||
* @since 4.2
|
||||
*
|
||||
*/
|
||||
@@ -71,9 +73,9 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
|
||||
private String metricsFactoryBeanName;
|
||||
|
||||
private String[] enabledCountsPatterns = { };
|
||||
private String[] enabledCountsPatterns = { };
|
||||
|
||||
private String[] enabledStatsPatterns = { };
|
||||
private String[] enabledStatsPatterns = { };
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
@@ -227,7 +229,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
AbstractMessageChannelMetrics metrics = this.metricsFactory.createChannelMetrics(name);
|
||||
Assert.state(metrics != null, "'metrics' must not be null");
|
||||
ManagementOverrides overrides = bean.getOverrides();
|
||||
Boolean enabled = smartMatch(this.enabledCountsPatterns, name);
|
||||
Boolean enabled = PatternMatchUtils.smartMatch(name, this.enabledCountsPatterns);
|
||||
if (enabled != null) {
|
||||
bean.setCountsEnabled(enabled);
|
||||
}
|
||||
@@ -236,7 +238,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
bean.setCountsEnabled(this.defaultCountsEnabled);
|
||||
}
|
||||
}
|
||||
enabled = smartMatch(this.enabledStatsPatterns, name);
|
||||
enabled = PatternMatchUtils.smartMatch(name, this.enabledStatsPatterns);
|
||||
if (enabled != null) {
|
||||
bean.setStatsEnabled(enabled);
|
||||
metrics.setFullStatsEnabled(enabled);
|
||||
@@ -258,7 +260,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
AbstractMessageHandlerMetrics metrics = this.metricsFactory.createHandlerMetrics(name);
|
||||
Assert.state(metrics != null, "'metrics' must not be null");
|
||||
ManagementOverrides overrides = bean.getOverrides();
|
||||
Boolean enabled = smartMatch(this.enabledCountsPatterns, name);
|
||||
Boolean enabled = PatternMatchUtils.smartMatch(name, this.enabledCountsPatterns);
|
||||
if (enabled != null) {
|
||||
bean.setCountsEnabled(enabled);
|
||||
}
|
||||
@@ -267,7 +269,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
bean.setCountsEnabled(this.defaultCountsEnabled);
|
||||
}
|
||||
}
|
||||
enabled = smartMatch(this.enabledStatsPatterns, name);
|
||||
enabled = PatternMatchUtils.smartMatch(name, this.enabledStatsPatterns);
|
||||
if (enabled != null) {
|
||||
bean.setStatsEnabled(enabled);
|
||||
metrics.setFullStatsEnabled(enabled);
|
||||
@@ -286,7 +288,7 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
}
|
||||
|
||||
private void configureSourceMetrics(String name, MessageSourceMetrics bean) {
|
||||
Boolean enabled = smartMatch(this.enabledCountsPatterns, name);
|
||||
Boolean enabled = PatternMatchUtils.smartMatch(name, this.enabledCountsPatterns);
|
||||
if (enabled != null) {
|
||||
bean.setCountsEnabled(enabled);
|
||||
}
|
||||
@@ -298,33 +300,6 @@ public class IntegrationManagementConfigurer implements SmartInitializingSinglet
|
||||
this.sourcesByName.put(bean.getManagedName() != null ? bean.getManagedName() : name, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple pattern match against the supplied patterns; also supports negated ('!')
|
||||
* patterns. First match wins (positive or negative).
|
||||
* @param patterns the patterns.
|
||||
* @param name the name to match.
|
||||
* @return null if no match; true for positive match; false for negative match.
|
||||
*/
|
||||
private Boolean smartMatch(String[] patterns, String name) {
|
||||
if (patterns != null) {
|
||||
for (String pattern : patterns) {
|
||||
boolean reverse = false;
|
||||
String patternToUse = pattern;
|
||||
if (pattern.startsWith("!")) {
|
||||
reverse = true;
|
||||
patternToUse = pattern.substring(1);
|
||||
}
|
||||
else if (pattern.startsWith("\\")) {
|
||||
patternToUse = pattern.substring(1);
|
||||
}
|
||||
if (PatternMatchUtils.simpleMatch(patternToUse, name)) {
|
||||
return !reverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null; //NOSONAR - intentional null return
|
||||
}
|
||||
|
||||
public String[] getChannelNames() {
|
||||
return this.channelsByName.keySet().toArray(new String[this.channelsByName.size()]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2017 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 org.springframework.integration.util;
|
||||
|
||||
/**
|
||||
* Utility methods for pattern matching.
|
||||
* This utilities provide support of negative pattern matching as well
|
||||
* unlike {@link org.springframework.util.PatternMatchUtils}.
|
||||
*
|
||||
* @author Meherzad Lahewala
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
* @see org.springframework.util.PatternMatchUtils
|
||||
*/
|
||||
public final class PatternMatchUtils {
|
||||
|
||||
private PatternMatchUtils() { }
|
||||
|
||||
/**
|
||||
* Pattern match against the supplied patterns; also supports negated ('!')
|
||||
* patterns. First match wins (positive or negative).
|
||||
* To match the names starting with {@code !} symbol,
|
||||
* you have to escape it prepending with the {@code \} symbol in the pattern definition.
|
||||
* @param str the string to match.
|
||||
* @param patterns the patterns.
|
||||
* @return true for positive match; false for negative; null if no pattern matches.
|
||||
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String[], String)
|
||||
*/
|
||||
public static Boolean smartMatch(String str, String... patterns) {
|
||||
if (patterns != null) {
|
||||
for (String pattern : patterns) {
|
||||
boolean reverse = false;
|
||||
String patternToUse = pattern;
|
||||
if (pattern.startsWith("!")) {
|
||||
reverse = true;
|
||||
patternToUse = pattern.substring(1);
|
||||
}
|
||||
else if (pattern.startsWith("\\")) {
|
||||
patternToUse = pattern.substring(1);
|
||||
}
|
||||
if (org.springframework.util.PatternMatchUtils.simpleMatch(patternToUse, str)) {
|
||||
return !reverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null; //NOSONAR - intentional null return
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4349,10 +4349,10 @@
|
||||
<xsd:attribute name="pattern" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
[REQUIRED] Channel name(s) or patterns. To specify more than one channel use
|
||||
','
|
||||
(e.g.,
|
||||
channel-name-pattern="input*, foo, bar")
|
||||
[REQUIRED] Channel name(s) or patterns. To specify more than one channel use ','.
|
||||
A leading '!' negates the pattern match ('!foo*' means don't add
|
||||
interceptor where the name matches the pattern 'foo*').
|
||||
For example "input*, foo, bar, !Foo*".
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
Reference in New Issue
Block a user