diff --git a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusAutoConfiguration.java b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusAutoConfiguration.java index ea4cd9d..2b3aaab 100644 --- a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusAutoConfiguration.java +++ b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/BusAutoConfiguration.java @@ -20,6 +20,7 @@ package org.springframework.cloud.bus; import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.trace.http.HttpTraceRepository; @@ -54,6 +55,7 @@ import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; +import org.springframework.core.env.Environment; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.AntPathMatcher; @@ -73,6 +75,8 @@ public class BusAutoConfiguration implements ApplicationEventPublisherAware { public static final String BUS_PATH_MATCHER_NAME = "busPathMatcher"; + public static final String CLOUD_CONFIG_NAME_PROPERTY = "spring.cloud.config.name"; + private MessageChannel cloudBusOutboundChannel; private ApplicationEventPublisher applicationEventPublisher; @@ -182,8 +186,11 @@ public class BusAutoConfiguration implements ApplicationEventPublisherAware { @Bean public ServiceMatcher serviceMatcher(@BusPathMatcher PathMatcher pathMatcher, - BusProperties properties) { - ServiceMatcher serviceMatcher = new ServiceMatcher(pathMatcher, properties.getId()); + BusProperties properties, Environment environment) { + String[] configNames = environment.getProperty(CLOUD_CONFIG_NAME_PROPERTY, + String[].class, new String[] {}); + ServiceMatcher serviceMatcher = new ServiceMatcher(pathMatcher, + properties.getId(), configNames); return serviceMatcher; } diff --git a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ServiceMatcher.java b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ServiceMatcher.java index 6dbe3dd..d399c9b 100644 --- a/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ServiceMatcher.java +++ b/spring-cloud-bus/src/main/java/org/springframework/cloud/bus/ServiceMatcher.java @@ -26,12 +26,27 @@ import org.springframework.util.PathMatcher; public class ServiceMatcher { private final PathMatcher matcher; private final String id; + private String[] configNames = new String[] {}; public ServiceMatcher(PathMatcher matcher, String id) { this.matcher = matcher; this.id = id; } + public ServiceMatcher(PathMatcher matcher, String id, String[] configNames) { + this(matcher, id); + + int colonIndex = id.indexOf(":"); + if (colonIndex >= 0) { + // if the id contains profiles and port, append them to the config names + String profilesAndPort = id.substring(colonIndex); + for (int i = 0; i < configNames.length; i++) { + configNames[i] = configNames[i] + profilesAndPort; + } + } + this.configNames = configNames; + } + public boolean isFromSelf(RemoteApplicationEvent event) { String originService = event.getOriginService(); String serviceId = getServiceId(); @@ -40,8 +55,19 @@ public class ServiceMatcher { public boolean isForSelf(RemoteApplicationEvent event) { String destinationService = event.getDestinationService(); - return (destinationService == null || destinationService.trim().isEmpty() - || this.matcher.match(destinationService, getServiceId())); + if (destinationService == null || destinationService.trim().isEmpty() + || this.matcher.match(destinationService, getServiceId())) { + return true; + } + + // Check all potential config names instead of service name + for (String configName : this.configNames) { + if (this.matcher.match(destinationService, configName)) { + return true; + } + } + + return false; } public String getServiceId() { diff --git a/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/ServiceMatcherWithConfigNamesTests.java b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/ServiceMatcherWithConfigNamesTests.java new file mode 100644 index 0000000..de9412f --- /dev/null +++ b/spring-cloud-bus/src/test/java/org/springframework/cloud/bus/ServiceMatcherWithConfigNamesTests.java @@ -0,0 +1,143 @@ +/* + * Copyright 2013-2019 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.cloud.bus; + +import java.util.Collections; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent; +import org.springframework.util.AntPathMatcher; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author Stefan Pfeiffer + * + */ +public class ServiceMatcherWithConfigNamesTests { + + private static final Map EMPTY_MAP = Collections.emptyMap(); + + private ServiceMatcher matcher; + + @Before + public void init() { + initMatcher("otherid:two:8888", new String[] {"one", "three"}); + } + + private void initMatcher(String id, String[] configNames) { + BusProperties properties = new BusProperties(); + properties.setId(id); + DefaultBusPathMatcher pathMatcher = new DefaultBusPathMatcher(new AntPathMatcher(":")); + matcher = new ServiceMatcher(pathMatcher, properties.getId(), configNames); + } + + @Test + public void forSelfWithWildcard() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one:two:*", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithWildcardAndOtherConfigName() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "three:two:*", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithGlobalWildcard() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "**", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithWildcardName() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "o*", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithWildcardNameAndProfile() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "o*:t*", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithWildcardString() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "o*", EMPTY_MAP)), is(true)); + } + + @Test + public void notForSelfWithWildCardNameAndMismatchingProfile() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "o*:f*", EMPTY_MAP)), is(false)); + } + + @Test + public void forSelfWithDoubleWildcard() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one:**", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithNoWildcard() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one", EMPTY_MAP)), is(true)); + } + + @Test + public void forSelfWithProfileNoWildcard() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one:two", EMPTY_MAP)), is(true)); + } + + @Test + public void notForSelf() { + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one:two:9999", EMPTY_MAP)), is(false)); + } + + @Test + public void forSelfWithMultipleProfiles() { + initMatcher("customerportal:dev,cloud:80", new String[] {"one", "three"}); + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "one:cloud:*", EMPTY_MAP)), is(true)); + } + + @Test + public void notForSelfWithMultipleProfiles() { + initMatcher("customerportal:dev,cloud:80", new String[] {"one", "three"}); + assertThat(matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "bar:cloud:*", EMPTY_MAP)), is(false)); + } + + @Test + public void notForSelfWithMultipleProfilesDifferentPort() { + initMatcher("customerportal:dev,cloud:80", new String[] {"one", "three"}); + assertThat( + matcher.isForSelf(new EnvironmentChangeRemoteApplicationEvent(this, + "foo:bar:spam", "customerportal:cloud:8008", EMPTY_MAP)), + is(false)); + } + +}