Merge branch 'master' into turbine2

Conflicts:
	pom.xml
This commit is contained in:
Spencer Gibb
2014-11-10 16:39:20 -07:00
10 changed files with 478 additions and 180 deletions

View File

@@ -20,3 +20,115 @@ spring:
----
The bus currently supports sending messages to all nodes listening or all nodes for a particular service (as defined by Eureka). More selector criteria will be added in the future (ie. only service X nodes in data center Y, etc...). The http endpoints are under the `/bus/*` actuator namespace. There are currently two implemented. The first, `/bus/env`, sends key/values pairs to update each nodes Spring Environment. The second, `/bus/refresh`, will reload each application's configuration, just as if they had all been pinged on their `/refresh` endpoint.
== Building
== Basic Compile and Test
To build the source you will need to install
http://maven.apache.org/run-maven/index.html[Apache Maven] v3.0.6 or above and JDK 1.7.
Spring Cloud uses Maven for most build-related activities, and you
should be able to get off the ground quite quickly by cloning the
project you are interested in and typing
----
$ mvn install -s .settings.xml
----
NOTE: You may need to increase the amount of memory available to Maven by setting
a `MAVEN_OPTS` environment variable with the value `-Xmx512m -XX:MaxPermSize=128m`
The `.settings.xml` is only required the first time (or after updates
to dependencies). It is there to provide repository declarations so
that those do not need to be hard coded in the project poms.
For hints on how to build the project look in `.travis.yml` if there
is one. There should be a "script" and maybe "install" command. Also
look at the "services" section to see if any services need to be
running locally (e.g. mongo or rabbit). Ignore the git-related bits
that you might find in "before_install" since they will be able git
credentials and you already have those.
If you need mongo, rabbit or redis, see the README in the [scripts
demo repository](https://github.com/spring-cloud-samples/scripts) for
instructions. For example consider using the "fig.yml" with
[Fig](http://www.fig.sh/) to run them in Docker containers.
== Documentation
The spring-cloud-build module has a "docs" profile, and if you switch
that on it will try to build asciidoc sources from
`src/main/asciidoc`. As part of that process it will look for a
`README.adoc` and process it by loading all the includes, but not
parsing or rendering it, just copying it to `${main.basedir}`
(defaults to `${basedir}`, i.e. the root of the project). If there are
any changes in the README it will then show up after a Maven build as
a modified file in the correct place. Just commit it and push the change.
== Pull Requests
Spring Cloud is released under the non-restrictive Apache 2.0 license,
and follows a very standard Github development process, using Github
tracker for issues and merging pull requests into master. If you want
to contribute even something trivial please do not hesitate, but
follow the guidelines below.
=== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you
to sign the
https://support.springsource.com/spring_committer_signup[contributor's
agreement]. Signing the contributor's agreement does not grant anyone
commit rights to the main repository, but it does mean that we can
accept your contributions, and you will get an author credit if we do.
Active contributors might be asked to join the core team, and given
the ability to merge pull requests.
=== Code Conventions and Housekeeping
None of these is essential for a pull request, but they will all help. They can also be
added after the original pull request but before a merge.
* Use the Spring Framework code format conventions. If you use Eclipse and you follow
the ``Importing into eclipse'' instructions below you should get project specific
formatting automatically. You can also import formatter settings using the
`eclipse-code-formatter.xml` file from the `eclipse` folder. If using IntelliJ, you can
use the http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter Plugin]
to import the same file.
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
`@author` tag identifying you, and preferably at least a paragraph on what the class is
for.
* Add the ASF license header comment to all new `.java` files (copy from existing files
in the project)
* Add yourself as an `@author` to the .java files that you modify substantially (more
than cosmetic changes).
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
* A few unit tests would help a lot as well -- someone has to do it.
* If no-one else is using your branch, please rebase it against the current master (or
other target branch in the main project).
=== Working with the code
If you don't have an IDE preference we would recommend that you use
http://www.springsource.com/developer/sts[Spring Tools Suite] or
http://eclipse.org[Eclipse] when working with the code. We use the
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
should also work without issue.
=== Importing into eclipse with m2eclipse
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
marketplace".
=== Importing into eclipse without m2eclipse
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
following command:
[indent=0]
----
$ mvn eclipse:eclipse
----
The generated eclipse projects can be imported by selecting `import existing projects`
from the `file` menu.
=== Importing into other IDEs
Maven is well supported by most Java IDEs. Refer to you vendor documentation.

View File

@@ -1,5 +1,6 @@
package org.springframework.cloud.bus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -8,52 +9,167 @@ import org.springframework.cloud.bus.endpoint.EnvironmentBusEndpoint;
import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint;
import org.springframework.cloud.bus.event.EnvironmentChangeListener;
import org.springframework.cloud.bus.event.RefreshListener;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import org.springframework.cloud.config.client.RefreshEndpoint;
import org.springframework.cloud.context.environment.EnvironmentManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.config.GlobalChannelInterceptor;
import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer;
import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
/**
* @author Spencer Gibb
* @author Dave Syer
*/
@Configuration
@ConditionalOnExpression("${bus.enabled:true}")
public class BusAutoConfiguration {
@Bean
public BusEndpoint busEndpoint() {
return new BusEndpoint();
}
@Autowired
private ConfigurableEnvironment environment;
@ConditionalOnClass(RefreshEndpoint.class)
@ConditionalOnBean(RefreshEndpoint.class)
protected static class BusRefreshConfiguration {
@Bean
@ConditionalOnExpression("${bus.refresh.enabled:true}")
public RefreshListener refreshListener() {
return new RefreshListener();
}
@Autowired
private ConfigurableApplicationContext context;
@Bean
@ConditionalOnExpression("${endpoints.bus.refresh.enabled:true}")
public RefreshBusEndpoint refreshBusEndpoint() {
return new RefreshBusEndpoint();
}
}
@Bean
public SubscribableChannel cloudBusOutboundChannel() {
return new DirectChannel();
}
@ConditionalOnClass(EnvironmentManager.class)
@ConditionalOnBean(EnvironmentManager.class)
protected static class BusEnvironmentConfiguration {
@Bean
@ConditionalOnExpression("${bus.env.enabled:true}")
public EnvironmentChangeListener environmentChangeListener() {
return new EnvironmentChangeListener();
}
// TODO: is there a way to move these filters to rabbit while not losing the
// information once it is published to spring?
@Bean
public GenericSelector<?> outboundFilter() {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent source) {
return isFromSelf(source);
}
};
}
@SuppressWarnings("unchecked")
private ApplicationEventListeningMessageProducer cloudBusOutboundMessageProducer() {
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
producer.setEventTypes(RemoteApplicationEvent.class);
return producer;
}
@Bean
public IntegrationFlow cloudBusOutboundFlow() {
ApplicationEventListeningMessageProducer producer = cloudBusOutboundMessageProducer();
// Workaround for bug in IntegrationFlow (it won't register the listener)
context.addApplicationListener(producer);
return IntegrationFlows.from(producer)
.filter(outboundFilter()).channel(cloudBusOutboundChannel()).get();
}
@Bean
public MessageChannel cloudBusInboundChannel() {
return new DirectChannel();
}
@Bean
public GenericSelector<?> inboundFilter() {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent event) {
return !isFromSelf(event) && isForSelf(event);
}
};
}
@Bean
public IntegrationFlow cloudBusInboundFlow() {
ApplicationEventPublishingMessageHandler messageHandler = new ApplicationEventPublishingMessageHandler();
return IntegrationFlows.from(cloudBusInboundChannel()).filter(inboundFilter())
.handle(messageHandler).get();
}
@Bean
@GlobalChannelInterceptor(patterns = "cloudBusInboundFlow*")
public WireTap wireTap() {
return new WireTap(cloudBusWiretapChannel());
}
@Bean
public DirectChannel cloudBusWiretapChannel() {
return MessageChannels.direct().get();
}
@Bean
public IntegrationFlow loggingFlow() {
LoggingHandler handler = new LoggingHandler("INFO");
handler.setShouldLogFullMessage(true);
return IntegrationFlows.from(cloudBusWiretapChannel()).handle(handler).get();
}
@Bean
public BusEndpoint busEndpoint() {
return new BusEndpoint();
}
@ConditionalOnClass(RefreshEndpoint.class)
@ConditionalOnBean(RefreshEndpoint.class)
protected static class BusRefreshConfiguration {
@Bean
@ConditionalOnExpression("${bus.refresh.enabled:true}")
public RefreshListener refreshListener() {
return new RefreshListener();
}
@Bean
@ConditionalOnExpression("${endpoints.bus.refresh.enabled:true}")
public RefreshBusEndpoint refreshBusEndpoint(ApplicationContext context,
BusEndpoint busEndpoint) {
return new RefreshBusEndpoint(context, context.getId(), busEndpoint);
}
}
@ConditionalOnClass(EnvironmentManager.class)
@ConditionalOnBean(EnvironmentManager.class)
protected static class BusEnvironmentConfiguration {
@Bean
@ConditionalOnExpression("${bus.env.enabled:true}")
public EnvironmentChangeListener environmentChangeListener() {
return new EnvironmentChangeListener();
}
@Bean
@ConditionalOnExpression("${endpoints.bus.env.enabled:true}")
public EnvironmentBusEndpoint environmentBusEndpoint(ApplicationContext context,
BusEndpoint busEndpoint) {
return new EnvironmentBusEndpoint(context, context.getId(), busEndpoint);
}
}
private boolean isFromSelf(RemoteApplicationEvent event) {
String originService = event.getOriginService();
String appName = getAppName();
return originService.equals(appName);
}
private boolean isForSelf(RemoteApplicationEvent event) {
return (event.getDestinationService() == null
|| event.getDestinationService().trim().isEmpty() || event
.getDestinationService().equals(getAppName()));
}
private String getAppName() {
return context.getId();
}
@Bean
@ConditionalOnExpression("${endpoints.bus.env.enabled:true}")
public EnvironmentBusEndpoint environmentBusEndpoint() {
return new EnvironmentBusEndpoint();
}
}
}

View File

@@ -7,24 +7,15 @@ import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.config.GlobalChannelInterceptor;
import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.amqp.Amqp;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer;
import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.messaging.MessageChannel;
/**
* @author Spencer Gibb
@@ -34,116 +25,48 @@ import org.springframework.integration.handler.LoggingHandler;
@ConditionalOnExpression("${bus.amqp.enabled:true}")
public class AmqpBusAutoConfiguration {
public static final String SPRING_CLOUD_BUS = "spring.cloud.bus";
public static final String SPRING_CLOUD_BUS = "spring.cloud.bus";
@Autowired
private ConnectionFactory connectionFactory;
@Autowired
private ConnectionFactory connectionFactory;
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
private AmqpTemplate amqpTemplate;
@Autowired
private AmqpTemplate amqpTemplate;
@Autowired
private ConfigurableEnvironment env;
//TODO: how to fail gracefully if no rabbit?
@Bean
protected FanoutExchange cloudBusExchange() {
//TODO: change to TopicExchange?
FanoutExchange exchange = new FanoutExchange(SPRING_CLOUD_BUS);
amqpAdmin.declareExchange(exchange);
return exchange;
}
@Bean
protected Queue localCloudBusQueue() {
Queue queue = amqpAdmin.declareQueue();
amqpAdmin.declareBinding(BindingBuilder.bind(queue).to(cloudBusExchange()));
return queue;
}
@SuppressWarnings("unchecked")
// TODO: how to fail gracefully if no rabbit?
@Bean
public ApplicationEventListeningMessageProducer cloudBusProducer() {
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
producer.setEventTypes(RemoteApplicationEvent.class);
producer.setOutputChannel(new DirectChannel());
return producer;
}
protected FanoutExchange cloudBusExchange() {
// TODO: change to TopicExchange?
FanoutExchange exchange = new FanoutExchange(SPRING_CLOUD_BUS);
amqpAdmin.declareExchange(exchange);
return exchange;
}
@Bean
public IntegrationFlow cloudBusOutboundFlow() {
return IntegrationFlows.from(cloudBusProducer())
.filter(outboundFilter())
.handle(Amqp.outboundAdapter(this.amqpTemplate).exchangeName(SPRING_CLOUD_BUS))
.get();
}
@Bean
protected Queue localCloudBusQueue() {
Queue queue = amqpAdmin.declareQueue();
amqpAdmin.declareBinding(BindingBuilder.bind(queue).to(cloudBusExchange()));
return queue;
}
//TODO: is there a way to move these filters to rabbit while not loosing the information once it is published to spring?
@Bean
public GenericSelector<?> outboundFilter() {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent source) {
return isFromSelf(source);
}
};
}
@Bean
public IntegrationFlow cloudBusAmqpOutboundFlow(
@Qualifier("cloudBusOutboundChannel") MessageChannel cloudBusOutboundChannel) {
return IntegrationFlows
.from(cloudBusOutboundChannel)
.handle(Amqp.outboundAdapter(this.amqpTemplate).exchangeName(
SPRING_CLOUD_BUS)).get();
}
@Bean
public GenericSelector<?> inboundFilter() {
return new GenericSelector<RemoteApplicationEvent>() {
@Override
public boolean accept(RemoteApplicationEvent event) {
return !isFromSelf(event) && isForSelf(event);
}
};
}
@Bean
public IntegrationFlow cloudBusAmqpInboundFlow(
@Qualifier("cloudBusInboundChannel") MessageChannel cloudBusInboundChannel) {
return IntegrationFlows
.from(Amqp.inboundAdapter(connectionFactory, localCloudBusQueue()))
.channel(cloudBusInboundChannel).get();
}
private boolean isForSelf(RemoteApplicationEvent event) {
return (event.getDestinationService() == null
|| event.getDestinationService().trim().isEmpty()
|| event.getDestinationService().equals(getAppName()));
}
private boolean isFromSelf(RemoteApplicationEvent event) {
String originService = event.getOriginService();
String appName = getAppName();
return originService.equals(appName);
}
private String getAppName() {
return env.getProperty("spring.application.name");
}
@Bean
public IntegrationFlow cloudBusInboundFlow(Environment env) {
ApplicationEventPublishingMessageHandler messageHandler = new ApplicationEventPublishingMessageHandler();
return IntegrationFlows.from(Amqp.inboundAdapter(connectionFactory, localCloudBusQueue()))
.filter(inboundFilter())
.handle(messageHandler)
.get();
}
@Bean
public DirectChannel wiretapChannel() {
return MessageChannels.direct().get();
}
@Bean
@GlobalChannelInterceptor(patterns = "cloudBusInboundFlow*")
public WireTap wireTap() {
return new WireTap(wiretapChannel());
}
@Bean
public IntegrationFlow loggingFlow() {
LoggingHandler handler = new LoggingHandler("INFO");
handler.setShouldLogFullMessage(true);
return IntegrationFlows.from(wiretapChannel())
.handle(handler)
.get();
}
}

View File

@@ -1,44 +1,48 @@
package org.springframework.cloud.bus.endpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.context.ApplicationEventPublisher;
/**
* @author Spencer Gibb
*/
public class AbstractBusEndpoint implements MvcEndpoint {
@Autowired
protected ConfigurableEnvironment env;
@Autowired
protected ConfigurableApplicationContext context;
@Autowired
private BusEndpoint delegate;
protected String getAppName() {
return env.getProperty("spring.application.name");
}
private ApplicationEventPublisher context;
protected void publish(ApplicationEvent event) {
context.publishEvent(event);
}
private BusEndpoint delegate;
@Override
public String getPath() {
return "/" + this.delegate.getId();
}
private String appId;
@Override
public boolean isSensitive() {
return this.delegate.isSensitive();
}
public AbstractBusEndpoint(ApplicationEventPublisher context, String appId, BusEndpoint busEndpoint) {
this.context = context;
this.appId = appId;
this.delegate = busEndpoint;
}
@Override
@SuppressWarnings("rawtypes")
public Class<? extends Endpoint> getEndpointType() {
return this.delegate.getClass();
}
protected String getInstanceId() {
return this.appId;
}
protected void publish(ApplicationEvent event) {
context.publishEvent(event);
}
@Override
public String getPath() {
return "/" + this.delegate.getId();
}
@Override
public boolean isSensitive() {
return this.delegate.isSensitive();
}
@Override
@SuppressWarnings("rawtypes")
public Class<? extends Endpoint> getEndpointType() {
return this.delegate.getClass();
}
}

View File

@@ -1,24 +1,29 @@
package org.springframework.cloud.bus.endpoint;
import java.util.Map;
import org.springframework.cloud.bus.event.EnvironmentChangeRemoteApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
/**
* @author Spencer Gibb
*/
public class EnvironmentBusEndpoint extends AbstractBusEndpoint {
public EnvironmentBusEndpoint(ApplicationEventPublisher context, String id, BusEndpoint delegate) {
super(context, id, delegate);
}
@RequestMapping(value = "env", method = RequestMethod.POST)
@ResponseBody
//TODO: make this an abstract method in AbstractBusEndpoint?
public void env(@RequestParam Map<String, String> params,
@RequestParam(value = "destination", required = false) String destination) {
publish(new EnvironmentChangeRemoteApplicationEvent(this, getAppName(), destination, params));
publish(new EnvironmentChangeRemoteApplicationEvent(this, getInstanceId(), destination, params));
}

View File

@@ -1,6 +1,7 @@
package org.springframework.cloud.bus.endpoint;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@@ -11,10 +12,14 @@ import org.springframework.web.bind.annotation.ResponseBody;
*/
public class RefreshBusEndpoint extends AbstractBusEndpoint {
@RequestMapping(value = "refresh", method = RequestMethod.POST)
public RefreshBusEndpoint(ApplicationEventPublisher context, String id, BusEndpoint delegate) {
super(context, id, delegate);
}
@RequestMapping(value = "refresh", method = RequestMethod.POST)
@ResponseBody
public void refresh(@RequestParam(value = "destination", required = false) String destination) {
publish(new RefreshRemoteApplicationEvent(this, getAppName(), destination));
publish(new RefreshRemoteApplicationEvent(this, getInstanceId(), destination));
}

View File

@@ -5,3 +5,7 @@ include::intro.adoc[]
== Quick Start
include::quickstart.adoc[]
== Building
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/src/main/asciidoc/building.adoc[]

View File

@@ -18,7 +18,7 @@ file = ARGV[0] if ARGV.length>0
srcDir = File.dirname(file)
out = "// Do not edit this file (e.g. go instead to src/main/asciidoc)\n\n"
doc = Asciidoctor.load_file file, safe: :safe, parse: false
doc = Asciidoctor.load_file file, safe: :safe, parse: false, attributes: 'allow-uri-read'
out << doc.reader.read
unless options[:to_file]

View File

@@ -0,0 +1,109 @@
package org.springframework.cloud.bus;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.bus.event.RefreshRemoteApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
public class BusAutoConfigurationTests {
private ConfigurableApplicationContext context;
@After
public void close() {
if (context != null) {
context.close();
}
}
@Test
public void inboundNotForSelf() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("foo");
context.getBean("cloudBusInboundChannel", MessageChannel.class)
.send(new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "bar",
"bar")));
assertNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Test
public void inboundFromSelf() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("foo");
context.getBean("cloudBusInboundChannel", MessageChannel.class)
.send(new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "foo",
null)));
assertNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Test
public void inboundNotFromSelf() {
context = SpringApplication.run(InboundMessageHandlerConfiguration.class);
context.setId("bar");
context.getBean("cloudBusInboundChannel", MessageChannel.class)
.send(new GenericMessage<>(new RefreshRemoteApplicationEvent(this, "foo",
null)));
assertNotNull(context.getBean(InboundMessageHandlerConfiguration.class).event);
}
@Test
public void outboundFromSelf() {
context = SpringApplication.run(OutboundMessageHandlerConfiguration.class);
context.setId("foo");
context.publishEvent(new RefreshRemoteApplicationEvent(this, "foo", null));
assertNotNull(context.getBean(OutboundMessageHandlerConfiguration.class).message);
}
@Test
public void outboundNotFromSelf() {
context = SpringApplication.run(OutboundMessageHandlerConfiguration.class);
context.setId("bar");
context.publishEvent(new RefreshRemoteApplicationEvent(this, "foo", null));
assertNull(context.getBean(OutboundMessageHandlerConfiguration.class).message);
}
@Configuration
@Import(BusAutoConfiguration.class)
@MessageEndpoint
@EnableIntegration
protected static class OutboundMessageHandlerConfiguration {
private Message<?> message;
@ServiceActivator(inputChannel = "cloudBusOutboundChannel")
public void handle(Message<?> message) {
this.message = message;
}
}
@Configuration
@Import(BusAutoConfiguration.class)
@MessageEndpoint
@EnableIntegration
protected static class InboundMessageHandlerConfiguration implements
ApplicationListener<RefreshRemoteApplicationEvent> {
private RefreshRemoteApplicationEvent event;
@Override
public void onApplicationEvent(RefreshRemoteApplicationEvent event) {
this.event = event;
}
}
}

View File

@@ -0,0 +1,20 @@
package org.springframework.cloud.bus.endpoint;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* @author Dave Syer
*/
public class RefreshBusEndpointTests {
@Test
public void instanceId() throws Exception {
RefreshBusEndpoint endpoint = new RefreshBusEndpoint(null, "foo", new BusEndpoint());
assertEquals("foo", endpoint.getInstanceId());
}
}