treat the 'destination' param as path while match using a PathMatcher

Specifically an AntPathMatcher with the separator as a colon (:)

fixes gh-17
This commit is contained in:
Spencer Gibb
2015-04-08 13:43:37 -06:00
parent 9593414c27
commit ca7aa9a49c
4 changed files with 92 additions and 16 deletions

View File

@@ -16,6 +16,10 @@ include::quickstart.adoc[]
The HTTP endpoints accept a "destination" parameter, e.g. "/bus/refresh?destination=customers:9000", where the destination is an `ApplicationContext` ID. If the ID is owned by an instance on the Bus then it will process the message and all other instances will ignore it. Spring Boot sets the ID for you in the `ContextIdApplicationContextInitializer` to a combination of the `spring.application.name`, active profiles and `server.port` by default.
== Addressing all instances of a service
The "destination" parameter is used in a Spring `PathMatcher` (with the path separator as a colon `:`) to determine if an instance will process the message. Using the example from above, "/bus/refresh?destination=customers:**" will target all instances of the "customers" service regardless of the profiles and ports set as the `ApplicationContext` ID.
== Customizing the AMQP ConnectionFactory
If you are using AMQP there needs to be a `ConnectionFactory` (from

View File

@@ -30,6 +30,8 @@ import org.springframework.integration.event.outbound.ApplicationEventPublishing
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
* @author Spencer Gibb
@@ -54,7 +56,7 @@ public class BusAutoConfiguration {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent source) {
return isFromSelf(source);
return serviceMatcher().isFromSelf(source);
}
};
}
@@ -85,7 +87,7 @@ public class BusAutoConfiguration {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent event) {
return !isFromSelf(event) && isForSelf(event);
return !serviceMatcher().isFromSelf(event) && serviceMatcher().isForSelf(event);
}
};
}
@@ -115,6 +117,16 @@ public class BusAutoConfiguration {
return IntegrationFlows.from(cloudBusWiretapChannel()).handle(handler).get();
}
@Bean
public PathMatcher busPathMatcher() {
return new AntPathMatcher(":");
}
@Bean
public ServiceMatcher serviceMatcher() {
return new ServiceMatcher();
}
@Configuration
@ConditionalOnClass(Endpoint.class)
protected static class BusEndpointConfiguration {
@@ -169,20 +181,6 @@ public class BusAutoConfiguration {
}
}
private boolean isFromSelf(RemoteApplicationEvent event) {
String originService = event.getOriginService();
String serviceId = getServiceId();
return originService.equals(serviceId);
}
private boolean isForSelf(RemoteApplicationEvent event) {
return (event.getDestinationService() == null
|| event.getDestinationService().trim().isEmpty() || event
.getDestinationService().equals(getServiceId()));
}
private String getServiceId() {
return context.getId();
}
}

View File

@@ -0,0 +1,44 @@
package org.springframework.cloud.bus;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.PathMatcher;
/**
* @author Spencer Gibb
*/
public class ServiceMatcher implements ApplicationContextAware {
private ApplicationContext context;
private PathMatcher matcher;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
@Autowired
@Qualifier("busPathMatcher")
public void setMatcher(PathMatcher matcher) {
this.matcher = matcher;
}
public boolean isFromSelf(RemoteApplicationEvent event) {
String originService = event.getOriginService();
String serviceId = getServiceId();
return matcher.match(originService, serviceId);
}
public boolean isForSelf(RemoteApplicationEvent event) {
String destinationService = event.getDestinationService();
return (destinationService == null || destinationService.trim().isEmpty() || matcher
.match(destinationService, getServiceId()));
}
private String getServiceId() {
return context.getId();
}
}

View File

@@ -75,6 +75,36 @@ public class BusAutoConfigurationTests {
assertNull(context.getBean(OutboundMessageHandlerConfiguration.class).message);
}
@Test
public void inboundNotFromSelfPathPattern() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("bar:1000");
context.getBean("cloudBusInboundChannel", MessageChannel.class).send(
new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "foo",
"bar:*")));
assertNotNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Test
public void inboundNotFromSelfDeepPathPattern() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("bar:test:1000");
context.getBean("cloudBusInboundChannel", MessageChannel.class).send(
new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "foo",
"bar:**")));
assertNotNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Test
public void inboundNotFromSelfFlatPattern() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("bar");
context.getBean("cloudBusInboundChannel", MessageChannel.class).send(
new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "foo",
"bar*")));
assertNotNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Configuration
@Import(BusAutoConfiguration.class)
@MessageEndpoint