diff --git a/README.adoc b/README.adoc index e209c6ebf..e52828529 100644 --- a/README.adoc +++ b/README.adoc @@ -2,15 +2,14 @@ image::https://travis-ci.org/spring-cloud/spring-cloud-stream.svg?branch=master[Build Status, link=https://travis-ci.org/spring-cloud/spring-cloud-stream] -This project allows a user to develop and run messaging microservices using Spring Integration and run them locally, or in the cloud, or even on Spring XD. Just create `MessageChannels` "input" and/or "output" and add `@EnableModule` and run your app as a Spring Boot app (single application context). You just need to connect to the physical broker for the bus, which is automatic if the relevant bus implementation is available on the classpath. The sample uses Redis. +This project allows a user to develop and run messaging microservices using Spring Integration and run them locally, or in the cloud, or even on Spring XD. Just add `@EnableModule` and run your app as a Spring Boot app (single application context). You just need to connect to the physical broker for the bus, which is automatic if the relevant bus implementation is available on the classpath. The sample uses Redis. Here's a sample source module (output channel only): [source,java] ---- @SpringBootApplication -@EnableModule -@ComponentScan(basePackageClasses=ModuleDefinition.class) +@ComponentScan(basePackageClasses=TimerSource.class) public class ModuleApplication { public static void main(String[] args) throws InterruptedException { @@ -20,18 +19,14 @@ public class ModuleApplication { } @Configuration -public class ModuleDefinition { +@EnableModule(Source.class) +public class TimerSource { @Value("${format}") private String format; @Bean - public MessageChannel output() { - return new DirectChannel(); - } - - @Bean - @InboundChannelAdapter(value = "output", autoStartup = "false", poller = @Poller(fixedDelay = "${fixedDelay}", maxMessagesPerPoll = "1")) + @InboundChannelAdapter(value = Source.OUTPUT, autoStartup = "false", poller = @Poller(fixedDelay = "${fixedDelay}", maxMessagesPerPoll = "1")) public MessageSource timerMessageSource() { return () -> new GenericMessage<>(new SimpleDateFormat(format).format(new Date())); } @@ -50,9 +45,42 @@ spring: outputChannelName: ${spring.application.name:ticker} ---- +`@EnableModule` is parameterized by an interface (in this case `Source`) which declares input and output channels. `Source`, `Sink` and `Processor` are provided off the shelf, but you can define others. Here's the definition of `Source` + +[source,java] +---- +public interface Source { + @Output("output") + MessageChannel output(); +} +---- + +The `@Output` annotation is used to identify output channels (messages leaving the module) and `@Input` is used to identify input channels (messages entering the module). It is optionally parameterized by a channel name - if the name is not provided the method name is used instead. An implementation of the interface is created for you and can be used in the application context by autowiring it, e.g. into a test case: + +[source,java] +---- +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ModuleApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Autowired + private Source source + + @Test + public void contextLoads() { + assertNotNull(this.sink.output()); + } + +} +---- + +NOTE: In this case there is only one `Source` in the application context so there is no need to qualify it when it is autowired. If there is ambiguity, e.g. if you are composing one module from some others, you can use `@ModuleChannels` qualifier to inject a specific channel set. The `@ModuleChannels` qualifier takes a parameter which is the class that carries the `@EnableModule` annotation (in this case the `TimerSource`). + == Multiple Input or Output Channels -A module can have multiple input or output channels. Instead of just one channel named "input" or "output" you can add multiple `MessageChannel` beans named `input.*` or `output.*` and the names are converted to external channel names on the broker. The external channel names are the `spring.cloud.streams.[input|output]ChannelName` plus the `MessageChannel` bean name, period separated. In addition, the bean name can be `input.[queue|topic|tap]:*` or `output.[queue|topic]:*` (i.e. with a channel type as a colon-separated prefix), and the semantics of the external bus channel changes accordingly (a tap is like a topic). For example, you can have two `MessageChannels` called "output" and "output.topic:foo" in a module with `outputChannelName=bar`, and the result is 2 external channels called "bar" and "topic:foo.bar". +A module can have multiple input or output channels all defined either as `@Input` and `@Output` methods in an interface (preferrable) or as bean definitions. Instead of just one channel named "input" or "output" you can add multiple `MessageChannel` methods annotated `input.*` or `output.*` and the names are converted to external channel names on the broker. The external channel names are the `spring.cloud.channels.[input|output]ChannelName` plus the `MessageChannel` bean name, period separated. In addition, the bean name can be `input.[queue|topic|tap]:*` or `output.[queue|topic]:*` (i.e. with a channel type as a colon-separated prefix), and the semantics of the external bus channel changes accordingly (a tap is like a topic). For example, you can have two `MessageChannels` called "output" and "output.topic:foo" in a module with `outputChannelName=bar`, and the result is 2 external channels called "bar" and "topic:foo.bar". == Samples