From 07991ce344c90ba7bd51f55cb2aa129c05f9505d Mon Sep 17 00:00:00 2001 From: Ilayaperumal Gopinathan Date: Tue, 16 Feb 2016 11:18:10 +0530 Subject: [PATCH] Move samples from spring-cloud-stream --- .gitignore | 25 ++++ README.adoc | 23 ++++ double/pom.xml | 59 ++++++++++ .../java/config/sink/SinkApplication.java | 26 +++++ .../config/sink/SinkModuleDefinition.java | 39 +++++++ .../java/config/source/SourceApplication.java | 26 +++++ .../config/source/SourceModuleDefinition.java | 45 +++++++ .../src/main/java/demo/DoubleApplication.java | 32 +++++ double/src/main/resources/application.yml | 1 + double/src/main/resources/sink.yml | 6 + double/src/main/resources/source.yml | 7 ++ .../java/demo/ModuleApplicationTests.java | 36 ++++++ extended/pom.xml | 70 +++++++++++ .../java/extended/ExtendedApplication.java | 46 ++++++++ extended/src/main/resources/application.yml | 6 + extended/src/main/resources/source.yml | 1 + .../java/demo/ModuleApplicationTests.java | 38 ++++++ multibinder-differentsystems/README.adoc | 12 ++ multibinder-differentsystems/pom.xml | 102 ++++++++++++++++ .../java/multibinder/BridgeTransformer.java | 33 ++++++ .../multibinder/MultibinderApplication.java | 29 +++++ .../src/main/resources/application.yml | 33 ++++++ .../TwoKafkaBindersApplicationTest.java | 110 ++++++++++++++++++ multibinder/pom.xml | 79 +++++++++++++ .../java/multibinder/BridgeTransformer.java | 33 ++++++ .../multibinder/MultibinderApplication.java | 29 +++++ .../src/main/resources/application.yml | 12 ++ .../RabbitAndRedisBinderApplicationTests.java | 94 +++++++++++++++ pom.xml | 63 ++++++++++ rxjava-processor/pom.xml | 62 ++++++++++ .../src/main/java/demo/RxJavaApplication.java | 33 ++++++ .../src/main/java/demo/RxJavaTransformer.java | 51 ++++++++ .../src/main/resources/application.yml | 8 ++ .../java/demo/ModuleApplicationTests.java | 36 ++++++ sink/README.md | 32 +++++ sink/pom.xml | 58 +++++++++ sink/src/main/java/demo/LogSink.java | 39 +++++++ sink/src/main/java/demo/SinkApplication.java | 29 +++++ sink/src/main/resources/application.yml | 12 ++ .../java/demo/ModuleApplicationTests.java | 55 +++++++++ source/README.md | 38 ++++++ source/pom.xml | 57 +++++++++ source/src/main/java/demo/DateFormat.java | 93 +++++++++++++++ .../src/main/java/demo/SourceApplication.java | 29 +++++ source/src/main/java/demo/TimeSource.java | 50 ++++++++ .../java/demo/TimeSourceOptionsMetadata.java | 103 ++++++++++++++++ source/src/main/resources/application.yml | 24 ++++ .../java/demo/ModuleApplicationTests.java | 36 ++++++ transform/pom.xml | 59 ++++++++++ .../main/java/demo/LoggingTransformer.java | 55 +++++++++ .../main/java/demo/TransformApplication.java | 29 +++++ transform/src/main/resources/application.yml | 9 ++ .../java/demo/ModuleApplicationTests.java | 36 ++++++ 53 files changed, 2148 insertions(+) create mode 100644 .gitignore create mode 100644 README.adoc create mode 100644 double/pom.xml create mode 100644 double/src/main/java/config/sink/SinkApplication.java create mode 100644 double/src/main/java/config/sink/SinkModuleDefinition.java create mode 100644 double/src/main/java/config/source/SourceApplication.java create mode 100644 double/src/main/java/config/source/SourceModuleDefinition.java create mode 100644 double/src/main/java/demo/DoubleApplication.java create mode 100644 double/src/main/resources/application.yml create mode 100644 double/src/main/resources/sink.yml create mode 100644 double/src/main/resources/source.yml create mode 100644 double/src/test/java/demo/ModuleApplicationTests.java create mode 100644 extended/pom.xml create mode 100644 extended/src/main/java/extended/ExtendedApplication.java create mode 100644 extended/src/main/resources/application.yml create mode 100644 extended/src/main/resources/source.yml create mode 100644 extended/src/test/java/demo/ModuleApplicationTests.java create mode 100644 multibinder-differentsystems/README.adoc create mode 100644 multibinder-differentsystems/pom.xml create mode 100644 multibinder-differentsystems/src/main/java/multibinder/BridgeTransformer.java create mode 100644 multibinder-differentsystems/src/main/java/multibinder/MultibinderApplication.java create mode 100644 multibinder-differentsystems/src/main/resources/application.yml create mode 100644 multibinder-differentsystems/src/test/java/multibinder/TwoKafkaBindersApplicationTest.java create mode 100644 multibinder/pom.xml create mode 100644 multibinder/src/main/java/multibinder/BridgeTransformer.java create mode 100644 multibinder/src/main/java/multibinder/MultibinderApplication.java create mode 100644 multibinder/src/main/resources/application.yml create mode 100644 multibinder/src/test/java/multibinder/RabbitAndRedisBinderApplicationTests.java create mode 100644 pom.xml create mode 100644 rxjava-processor/pom.xml create mode 100644 rxjava-processor/src/main/java/demo/RxJavaApplication.java create mode 100644 rxjava-processor/src/main/java/demo/RxJavaTransformer.java create mode 100644 rxjava-processor/src/main/resources/application.yml create mode 100644 rxjava-processor/src/test/java/demo/ModuleApplicationTests.java create mode 100644 sink/README.md create mode 100644 sink/pom.xml create mode 100644 sink/src/main/java/demo/LogSink.java create mode 100644 sink/src/main/java/demo/SinkApplication.java create mode 100644 sink/src/main/resources/application.yml create mode 100644 sink/src/test/java/demo/ModuleApplicationTests.java create mode 100644 source/README.md create mode 100644 source/pom.xml create mode 100644 source/src/main/java/demo/DateFormat.java create mode 100644 source/src/main/java/demo/SourceApplication.java create mode 100644 source/src/main/java/demo/TimeSource.java create mode 100644 source/src/main/java/demo/TimeSourceOptionsMetadata.java create mode 100644 source/src/main/resources/application.yml create mode 100644 source/src/test/java/demo/ModuleApplicationTests.java create mode 100644 transform/pom.xml create mode 100644 transform/src/main/java/demo/LoggingTransformer.java create mode 100644 transform/src/main/java/demo/TransformApplication.java create mode 100644 transform/src/main/resources/application.yml create mode 100644 transform/src/test/java/demo/ModuleApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eade3d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +/application.yml +/application.properties +asciidoctor.css +*~ +.#* +*# +target/ +build/ +bin/ +_site/ +.classpath +.project +.settings +.springBeans +.DS_Store +*.sw* +*.iml +*.ipr +*.iws +.idea/* +.factorypath +spring-xd-samples/*/xd +dump.rdb +.apt_generated +artifacts diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..b6578a3 --- /dev/null +++ b/README.adoc @@ -0,0 +1,23 @@ +== Samples + +There are several samples, all running on the redis transport (so you need redis running locally to test them). + + +NOTE: The main set of samples are "vanilla" in the sense that they are not deployable as XD modules by the current generation (1.x) of XD. You can still interact with an XD system using the appropriate naming convention for input and output channel names (`.` format). + +* `source` is a Java config version of the classic "timer" module from Spring XD. It has a "fixedDelay" option (in milliseconds) for the period between emitting messages. + +* `sink` is a Java config version of the classic "log" module from Spring XD. It has no options (but some could easily be added), and just logs incoming messages at INFO level. + +* `transform` is a simple pass through logging transformer (just logs the incoming message and passes it on). + +* `double` is a combination of 2 modules defined locally (a source and a sink, so the whole app is self contained). + +* `extended` is a multi-module mashup of `source | transform | transform | sink`, where the modules are defined in the other samples and referred to in this app just as dependencies. + +* `multibinder` is a sample application that shows how an application could use multiple binders. In this case, the processor's input/output channels connect to different brokers using their own binder configurations. + +* `multibinder-differentsystems` shows how an application could use same binder implementation but different configurations for its channels. In this case, a processor's input/output channels connect to same binder implementation but with two separate broker configurations. + +If you run the source and the sink and point them at the same redis instance (e.g. do nothing to get the one on localhost, or the one they are both bound to as a service on Cloud Foundry) then they will form a "stream" and start talking to each other. All the samples have friendly JMX and Actuator endpoints for inspecting what is going on in the system. + diff --git a/double/pom.xml b/double/pom.xml new file mode 100644 index 0000000..9a658f2 --- /dev/null +++ b/double/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + spring-cloud-stream-sample-double + jar + + spring-cloud-stream-sample-double + Demo project for Aggregate Builder + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + demo.DoubleApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/double/src/main/java/config/sink/SinkApplication.java b/double/src/main/java/config/sink/SinkApplication.java new file mode 100644 index 0000000..e093d1f --- /dev/null +++ b/double/src/main/java/config/sink/SinkApplication.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 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 config.sink; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Marius Bogoevici + */ +@SpringBootApplication +public class SinkApplication { +} diff --git a/double/src/main/java/config/sink/SinkModuleDefinition.java b/double/src/main/java/config/sink/SinkModuleDefinition.java new file mode 100644 index 0000000..995ff62 --- /dev/null +++ b/double/src/main/java/config/sink/SinkModuleDefinition.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 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 config.sink; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.integration.annotation.ServiceActivator; + +/** + * @author Dave Syer + * @author Marius Bogoevici + */ +@EnableBinding(Sink.class) +public class SinkModuleDefinition { + + private static Logger logger = LoggerFactory.getLogger(SinkModuleDefinition.class); + + @ServiceActivator(inputChannel=Sink.INPUT) + public void loggerSink(Object payload) { + logger.info("Received: " + payload); + } + +} diff --git a/double/src/main/java/config/source/SourceApplication.java b/double/src/main/java/config/source/SourceApplication.java new file mode 100644 index 0000000..f1d9077 --- /dev/null +++ b/double/src/main/java/config/source/SourceApplication.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 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 config.source; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Marius Bogoevici + */ +@SpringBootApplication +public class SourceApplication { +} diff --git a/double/src/main/java/config/source/SourceModuleDefinition.java b/double/src/main/java/config/source/SourceModuleDefinition.java new file mode 100644 index 0000000..2156887 --- /dev/null +++ b/double/src/main/java/config/source/SourceModuleDefinition.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 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 config.source; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Source; +import org.springframework.context.annotation.Bean; +import org.springframework.integration.annotation.InboundChannelAdapter; +import org.springframework.integration.annotation.Poller; +import org.springframework.integration.core.MessageSource; +import org.springframework.messaging.support.GenericMessage; + +/** + * @author Dave Syer + * @author Marius Bogoevici + */ +@EnableBinding(Source.class) +public class SourceModuleDefinition { + + private String format = "yyyy-MM-dd HH:mm:ss"; + + @Bean + @InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "${fixedDelay}", maxMessagesPerPoll = "1")) + public MessageSource timerMessageSource() { + return () -> new GenericMessage<>(new SimpleDateFormat(this.format).format(new Date())); + } + +} diff --git a/double/src/main/java/demo/DoubleApplication.java b/double/src/main/java/demo/DoubleApplication.java new file mode 100644 index 0000000..3ebe00b --- /dev/null +++ b/double/src/main/java/demo/DoubleApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 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 demo; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.aggregate.AggregateApplication; + +import config.sink.SinkApplication; +import config.source.SourceApplication; + +@SpringBootApplication +public class DoubleApplication { + + public static void main(String[] args) { + AggregateApplication.run(SourceApplication.class, SinkApplication.class); + } + +} diff --git a/double/src/main/resources/application.yml b/double/src/main/resources/application.yml new file mode 100644 index 0000000..d84e8d1 --- /dev/null +++ b/double/src/main/resources/application.yml @@ -0,0 +1 @@ +fixedDelay: 1000 diff --git a/double/src/main/resources/sink.yml b/double/src/main/resources/sink.yml new file mode 100644 index 0000000..a0f178e --- /dev/null +++ b/double/src/main/resources/sink.yml @@ -0,0 +1,6 @@ +spring: + cloud: + stream: + bindings: + input: testtock + \ No newline at end of file diff --git a/double/src/main/resources/source.yml b/double/src/main/resources/source.yml new file mode 100644 index 0000000..ac31a89 --- /dev/null +++ b/double/src/main/resources/source.yml @@ -0,0 +1,7 @@ +fixedDelay: 5000 +spring: + cloud: + stream: + bindings: + output: testtock + \ No newline at end of file diff --git a/double/src/test/java/demo/ModuleApplicationTests.java b/double/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..361e028 --- /dev/null +++ b/double/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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 demo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = DoubleApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/extended/pom.xml b/extended/pom.xml new file mode 100644 index 0000000..cc10812 --- /dev/null +++ b/extended/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + spring-cloud-stream-sample-extended + jar + spring-cloud-stream-sample-extended + Demo project for extended module using aggregate builder + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + extended.ExtendedApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-sample-source + + + org.springframework.cloud + spring-cloud-stream-sample-transform + + + org.springframework.cloud + spring-cloud-stream-sample-sink + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/extended/src/main/java/extended/ExtendedApplication.java b/extended/src/main/java/extended/ExtendedApplication.java new file mode 100644 index 0000000..b4c5e3d --- /dev/null +++ b/extended/src/main/java/extended/ExtendedApplication.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 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 extended; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.stream.aggregate.AggregateBuilder; +import org.springframework.cloud.stream.aggregate.AggregateConfigurer; + +import demo.LogSink; +import demo.LoggingTransformer; +import demo.TimeSource; + +@SpringBootApplication +public class ExtendedApplication implements AggregateConfigurer { + + @Override + public void configure(AggregateBuilder builder) { + // @formatter:off + builder + .from(TimeSource.class).as("source") + .via(LoggingTransformer.class) + .via(LoggingTransformer.class).profiles("other") + .to(LogSink.class); + // @formatter:on + } + + public static void main(String[] args) { + SpringApplication.run(ExtendedApplication.class, args); + } + +} diff --git a/extended/src/main/resources/application.yml b/extended/src/main/resources/application.yml new file mode 100644 index 0000000..a70e9bc --- /dev/null +++ b/extended/src/main/resources/application.yml @@ -0,0 +1,6 @@ +--- +spring: + profiles: other +module: + logging: + name: other \ No newline at end of file diff --git a/extended/src/main/resources/source.yml b/extended/src/main/resources/source.yml new file mode 100644 index 0000000..daf5166 --- /dev/null +++ b/extended/src/main/resources/source.yml @@ -0,0 +1 @@ +fixedDelay: 5000 diff --git a/extended/src/test/java/demo/ModuleApplicationTests.java b/extended/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..2683a46 --- /dev/null +++ b/extended/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 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 demo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import extended.ExtendedApplication; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = ExtendedApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/multibinder-differentsystems/README.adoc b/multibinder-differentsystems/README.adoc new file mode 100644 index 0000000..5c7c840 --- /dev/null +++ b/multibinder-differentsystems/README.adoc @@ -0,0 +1,12 @@ +== Spring Cloud Stream Multibinder Application with Different Systems + +This example shows how to run a Spring Cloud Stream application with the same binder type configured for two separate Kafka clusters. + +To run the example, command line parameters for the Zookeeper ensembles and Kafka clusters must be provided, as in the following example: + +```` +java -jar spring-cloud-stream-samples/multibinder-differentsystems/target/spring-cloud-stream-sample-multibinder-differentsystems-1.0.0.BUILD-SNAPSHOT-exec.jar --kafkaBroker1=localhost:9092 --zk1=localhost:2181 --kafkaBroker2=localhost:9093 --zk2=localhost:2182 +```` + +This assumes that two Kafka clusters and Zookeeper ensembles are running locally. Alternatively, the default values of `localhost:9092` and `localhost:2181` can be provided for both clusters. + diff --git a/multibinder-differentsystems/pom.xml b/multibinder-differentsystems/pom.xml new file mode 100644 index 0000000..35addd9 --- /dev/null +++ b/multibinder-differentsystems/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + spring-cloud-stream-sample-multibinder-differentsystems + jar + spring-cloud-stream-sample-multibinder-differentsystems + + Demo project for multiple binders of the same type (Kafka), connecting to different systems (broker groups) + + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + multibinder.MultibinderApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-sample-source + + + org.springframework.cloud + spring-cloud-stream-sample-transform + + + org.springframework.cloud + spring-cloud-stream-sample-sink + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.cloud + spring-cloud-stream-binder-kafka + + + org.springframework.cloud + spring-cloud-stream-test-support-internal + test + + + org.apache.kafka + kafka_2.10 + test + 0.8.2.1 + test + + + org.slf4j + slf4j-log4j12 + + + + + org.apache.curator + curator-test + 2.6.0 + test + + + org.slf4j + slf4j-log4j12 + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/multibinder-differentsystems/src/main/java/multibinder/BridgeTransformer.java b/multibinder-differentsystems/src/main/java/multibinder/BridgeTransformer.java new file mode 100644 index 0000000..d68720f --- /dev/null +++ b/multibinder-differentsystems/src/main/java/multibinder/BridgeTransformer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 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 multibinder; + +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.integration.annotation.ServiceActivator; + +/** + * @author Marius Bogoevici + */ +@EnableBinding(Processor.class) +public class BridgeTransformer { + + @ServiceActivator(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT) + public Object transform(Object payload) { + return payload; + } +} diff --git a/multibinder-differentsystems/src/main/java/multibinder/MultibinderApplication.java b/multibinder-differentsystems/src/main/java/multibinder/MultibinderApplication.java new file mode 100644 index 0000000..7aba5f6 --- /dev/null +++ b/multibinder-differentsystems/src/main/java/multibinder/MultibinderApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 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 multibinder; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MultibinderApplication { + + public static void main(String[] args) { + SpringApplication.run(MultibinderApplication.class, args); + } + +} diff --git a/multibinder-differentsystems/src/main/resources/application.yml b/multibinder-differentsystems/src/main/resources/application.yml new file mode 100644 index 0000000..ca88423 --- /dev/null +++ b/multibinder-differentsystems/src/main/resources/application.yml @@ -0,0 +1,33 @@ +server: + port: 8082 +spring: + cloud: + stream: + bindings: + input: + destination: dataIn + binder: kafka1 + output: + destination: dataOut + binder: kafka2 + binders: + kafka1: + type: kafka + environment: + spring: + cloud: + stream: + binder: + kafka: + brokers: ${kafkaBroker1} + zkNodes: ${zk1} + kafka2: + type: kafka + environment: + spring: + cloud: + stream: + binder: + kafka: + brokers: ${kafkaBroker2} + zkNodes: ${zk2} diff --git a/multibinder-differentsystems/src/test/java/multibinder/TwoKafkaBindersApplicationTest.java b/multibinder-differentsystems/src/test/java/multibinder/TwoKafkaBindersApplicationTest.java new file mode 100644 index 0000000..e8287d6 --- /dev/null +++ b/multibinder-differentsystems/src/test/java/multibinder/TwoKafkaBindersApplicationTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2015-2016 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 multibinder; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.hasSize; + +import java.util.List; +import java.util.UUID; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.stream.binder.BinderFactory; +import org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder; +import org.springframework.cloud.stream.test.junit.kafka.KafkaTestSupport; +import org.springframework.cloud.stream.test.junit.redis.RedisTestSupport; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.kafka.core.BrokerAddress; +import org.springframework.integration.kafka.core.Configuration; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = MultibinderApplication.class) +@WebAppConfiguration +@DirtiesContext +public class TwoKafkaBindersApplicationTest { + + @ClassRule + public static KafkaTestSupport kafkaTestSupport1 = new KafkaTestSupport(true); + + @ClassRule + public static KafkaTestSupport kafkaTestSupport2 = new KafkaTestSupport(true); + + @ClassRule + public static RedisTestSupport redisTestSupport = new RedisTestSupport(); + + @BeforeClass + public static void setupEnvironment() { + System.setProperty("kafkaBroker1", kafkaTestSupport1.getBrokerAddress()); + System.setProperty("zk1", kafkaTestSupport1.getZkConnectString()); + System.setProperty("kafkaBroker2", kafkaTestSupport2.getBrokerAddress()); + System.setProperty("zk2", kafkaTestSupport2.getZkConnectString()); + } + + @Autowired + private BinderFactory binderFactory; + + @Test + public void contextLoads() { + KafkaMessageChannelBinder kafka1 = (KafkaMessageChannelBinder) binderFactory.getBinder("kafka1"); + DirectFieldAccessor directFieldAccessor = new DirectFieldAccessor(kafka1.getConnectionFactory()); + Configuration configuration = (Configuration) directFieldAccessor.getPropertyValue("configuration"); + List brokerAddresses = configuration.getBrokerAddresses(); + Assert.assertThat(brokerAddresses, hasSize(1)); + Assert.assertThat(brokerAddresses, contains(BrokerAddress.fromAddress(kafkaTestSupport1.getBrokerAddress()))); + KafkaMessageChannelBinder kafka2 = (KafkaMessageChannelBinder) binderFactory.getBinder("kafka2"); + DirectFieldAccessor directFieldAccessor2 = new DirectFieldAccessor(kafka2.getConnectionFactory()); + Configuration configuration2 = (Configuration) directFieldAccessor2.getPropertyValue("configuration"); + List brokerAddresses2 = configuration2.getBrokerAddresses(); + Assert.assertThat(brokerAddresses2, hasSize(1)); + Assert.assertThat(brokerAddresses2, contains(BrokerAddress.fromAddress(kafkaTestSupport2.getBrokerAddress()))); + } + + @Test + public void messagingWorks() { + DirectChannel dataProducer = new DirectChannel(); + binderFactory.getBinder("kafka1").bindProducer("dataIn", dataProducer, null); + + QueueChannel dataConsumer = new QueueChannel(); + binderFactory.getBinder("kafka2").bindConsumer("dataOut", UUID.randomUUID().toString(), + dataConsumer, null); + + String testPayload = "testFoo" + UUID.randomUUID().toString(); + dataProducer.send(MessageBuilder.withPayload(testPayload).build()); + + Message receive = dataConsumer.receive(2000); + Assert.assertThat(receive, Matchers.notNullValue()); + Assert.assertThat(receive.getPayload(), CoreMatchers.equalTo(testPayload)); + } + +} diff --git a/multibinder/pom.xml b/multibinder/pom.xml new file mode 100644 index 0000000..36ebe1d --- /dev/null +++ b/multibinder/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + spring-cloud-stream-sample-multibinder + jar + spring-cloud-stream-sample-multibinder + Demo project for multiple binders of different types (Redis and Rabbit) + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + multibinder.MultibinderApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-sample-source + + + org.springframework.cloud + spring-cloud-stream-sample-transform + + + org.springframework.cloud + spring-cloud-stream-sample-sink + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.cloud + spring-cloud-stream-binder-rabbit + + + org.springframework.cloud + spring-cloud-stream-test-support-internal + test + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/multibinder/src/main/java/multibinder/BridgeTransformer.java b/multibinder/src/main/java/multibinder/BridgeTransformer.java new file mode 100644 index 0000000..d68720f --- /dev/null +++ b/multibinder/src/main/java/multibinder/BridgeTransformer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 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 multibinder; + +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.integration.annotation.ServiceActivator; + +/** + * @author Marius Bogoevici + */ +@EnableBinding(Processor.class) +public class BridgeTransformer { + + @ServiceActivator(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT) + public Object transform(Object payload) { + return payload; + } +} diff --git a/multibinder/src/main/java/multibinder/MultibinderApplication.java b/multibinder/src/main/java/multibinder/MultibinderApplication.java new file mode 100644 index 0000000..7aba5f6 --- /dev/null +++ b/multibinder/src/main/java/multibinder/MultibinderApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 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 multibinder; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MultibinderApplication { + + public static void main(String[] args) { + SpringApplication.run(MultibinderApplication.class, args); + } + +} diff --git a/multibinder/src/main/resources/application.yml b/multibinder/src/main/resources/application.yml new file mode 100644 index 0000000..9c3cb21 --- /dev/null +++ b/multibinder/src/main/resources/application.yml @@ -0,0 +1,12 @@ +server: + port: 8082 +spring: + cloud: + stream: + bindings: + input: + destination: dataIn + binder: redis + output: + destination: dataOut + binder: rabbit diff --git a/multibinder/src/test/java/multibinder/RabbitAndRedisBinderApplicationTests.java b/multibinder/src/test/java/multibinder/RabbitAndRedisBinderApplicationTests.java new file mode 100644 index 0000000..3170b00 --- /dev/null +++ b/multibinder/src/test/java/multibinder/RabbitAndRedisBinderApplicationTests.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015-2016 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 multibinder; + +import java.util.UUID; + +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.stream.binder.BinderFactory; +import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; +import org.springframework.cloud.stream.test.junit.redis.RedisTestSupport; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +/** + * @author Marius Bogoevici + * @author Gary Russell + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = MultibinderApplication.class) +@WebAppConfiguration +@DirtiesContext +public class RabbitAndRedisBinderApplicationTests { + + @ClassRule + public static RabbitTestSupport rabbitTestSupport = new RabbitTestSupport(); + + @ClassRule + public static RedisTestSupport redisTestSupport = new RedisTestSupport(); + + @Autowired + private BinderFactory binderFactory; + + private final String randomGroup = UUID.randomUUID().toString(); + + @After + public void cleanUp() { + RabbitAdmin admin = new RabbitAdmin(rabbitTestSupport.getResource()); + admin.deleteQueue("binder.dataOut.default"); + admin.deleteQueue("binder.dataOut." + this.randomGroup); + admin.deleteExchange("binder.dataOut"); + } + + @Test + public void contextLoads() { + } + + @Test + public void messagingWorks() { + DirectChannel dataProducer = new DirectChannel(); + binderFactory.getBinder("redis").bindProducer("dataIn", dataProducer, null); + + QueueChannel dataConsumer = new QueueChannel(); + binderFactory.getBinder("rabbit").bindConsumer("dataOut", this.randomGroup, + dataConsumer, null); + + String testPayload = "testFoo" + this.randomGroup; + dataProducer.send(MessageBuilder.withPayload(testPayload).build()); + + Message receive = dataConsumer.receive(2000); + Assert.assertThat(receive, Matchers.notNullValue()); + Assert.assertThat(receive.getPayload(), CoreMatchers.equalTo(testPayload)); + } + +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3d81d0f --- /dev/null +++ b/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + spring-cloud-stream-samples + pom + http://cloud.spring.io/spring-cloud-stream/ + + Pivotal Software, Inc. + http://www.spring.io + + + org.springframework.cloud + spring-cloud-stream-parent + 1.0.0.BUILD-SNAPSHOT + + + UTF-8 + 1.8 + + + source + sink + transform + double + extended + multibinder + multibinder-differentsystems + rxjava-processor + + + + + org.springframework.cloud + spring-cloud-stream-sample-source + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream-sample-sink + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream-sample-transform + 1.0.0.BUILD-SNAPSHOT + + + + + + + + + maven-deploy-plugin + + true + + + + + + diff --git a/rxjava-processor/pom.xml b/rxjava-processor/pom.xml new file mode 100644 index 0000000..3a21375 --- /dev/null +++ b/rxjava-processor/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + + spring-cloud-stream-sample-rxjava + jar + + spring-cloud-stream-sample-rxjava + Demo project for RxJava module + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + UTF-8 + demo.RxJavaApplication + 1.8 + + + + + org.springframework.cloud + spring-cloud-stream-rxjava + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/rxjava-processor/src/main/java/demo/RxJavaApplication.java b/rxjava-processor/src/main/java/demo/RxJavaApplication.java new file mode 100644 index 0000000..78fc214 --- /dev/null +++ b/rxjava-processor/src/main/java/demo/RxJavaApplication.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 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 demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +/** + * @author Ilayaperumal Gopinathan + */ +@SpringBootApplication +public class RxJavaApplication { + + public static void main(String[] args) { + SpringApplication.run(RxJavaApplication.class, args); + } + +} diff --git a/rxjava-processor/src/main/java/demo/RxJavaTransformer.java b/rxjava-processor/src/main/java/demo/RxJavaTransformer.java new file mode 100644 index 0000000..8ed8146 --- /dev/null +++ b/rxjava-processor/src/main/java/demo/RxJavaTransformer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 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 demo; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.stream.annotation.rxjava.EnableRxJavaProcessor; +import org.springframework.cloud.stream.annotation.rxjava.RxJavaProcessor; +import org.springframework.context.annotation.Bean; + +@EnableRxJavaProcessor +public class RxJavaTransformer { + + private static Logger logger = LoggerFactory.getLogger(RxJavaTransformer.class); + + @Bean + public RxJavaProcessor processor() { + return inputStream -> inputStream.map(data -> { + logger.info("Got data = " + data); + return data; + }).buffer(5).map(data -> String.valueOf(avg(data))); + } + + private static Double avg(List data) { + double sum = 0; + double count = 0; + for(String d : data) { + count++; + sum += Double.valueOf(d); + } + return sum/count; + } + +} diff --git a/rxjava-processor/src/main/resources/application.yml b/rxjava-processor/src/main/resources/application.yml new file mode 100644 index 0000000..8668638 --- /dev/null +++ b/rxjava-processor/src/main/resources/application.yml @@ -0,0 +1,8 @@ +server: + port: 8082 +spring: + cloud: + stream: + bindings: + output: xformed + input: testtock diff --git a/rxjava-processor/src/test/java/demo/ModuleApplicationTests.java b/rxjava-processor/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..4e6887b --- /dev/null +++ b/rxjava-processor/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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 demo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = RxJavaApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/sink/README.md b/sink/README.md new file mode 100644 index 0000000..12ae13a --- /dev/null +++ b/sink/README.md @@ -0,0 +1,32 @@ +Spring Cloud Stream Sink Sample +============================= + +In this *Spring Cloud Stream* sample, messages are received from a stream and the payload of each is logged to the console. + +## Requirements + +To run this sample, you will need to have installed: + +* Java 8 or Above + +This example requires Redis to be running on localhost. + +## Code Tour + +This sample is a Spring Boot application that uses Spring Cloud Stream to receive messages and write each payload to the console. The sink module has 2 primary components: + +* SinkApplication - the Spring Boot Main Application +* LogSink - the module that receives the data from the stream and writes it out to the console + +## Building with Maven + +Build the sample by executing: + + sink>$ mvn clean package + +## Running the Sample + +To start the sink module execute the following: + + sink>$ java -jar target/spring-cloud-stream-sample-sink-1.0.0.BUILD-SNAPSHOT-exec.jar + diff --git a/sink/pom.xml b/sink/pom.xml new file mode 100644 index 0000000..bb29a7f --- /dev/null +++ b/sink/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + spring-cloud-stream-sample-sink + jar + spring-cloud-stream-sample-sink + Demo project for Sink module + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + demo.SinkApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/sink/src/main/java/demo/LogSink.java b/sink/src/main/java/demo/LogSink.java new file mode 100644 index 0000000..8caaaf1 --- /dev/null +++ b/sink/src/main/java/demo/LogSink.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 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 demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.integration.annotation.ServiceActivator; + +/** + * @author Dave Syer + * + */ +@EnableBinding(Sink.class) +public class LogSink { + + private static Logger logger = LoggerFactory.getLogger(LogSink.class); + + @ServiceActivator(inputChannel=Sink.INPUT) + public void loggerSink(Object payload) { + logger.info("Received: " + payload); + } + +} diff --git a/sink/src/main/java/demo/SinkApplication.java b/sink/src/main/java/demo/SinkApplication.java new file mode 100644 index 0000000..8e2c3a1 --- /dev/null +++ b/sink/src/main/java/demo/SinkApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 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 demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SinkApplication { + + public static void main(String[] args) { + SpringApplication.run(SinkApplication.class, args); + } + +} diff --git a/sink/src/main/resources/application.yml b/sink/src/main/resources/application.yml new file mode 100644 index 0000000..d469a0f --- /dev/null +++ b/sink/src/main/resources/application.yml @@ -0,0 +1,12 @@ +server: + port: 8081 +spring: + cloud: + stream: + bindings: + input: testtock + # uncomment below to have this module consume from a specific partition + # in the range of 0 to N-1, where N is the upstream module's partitionCount + #consumerProperties: + # partitionIndex: 1 + \ No newline at end of file diff --git a/sink/src/test/java/demo/ModuleApplicationTests.java b/sink/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..f2a02f2 --- /dev/null +++ b/sink/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015 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 demo; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.stream.annotation.Bindings; +import org.springframework.cloud.stream.annotation.Output; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.stream.messaging.Source; +import org.springframework.messaging.MessageChannel; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SinkApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Autowired + @Bindings(LogSink.class) + private Sink sink; + + @Autowired + private Sink same; + + @Output(Source.OUTPUT) + private MessageChannel output; + + @Test + public void contextLoads() { + assertNotNull(this.sink.input()); + } + +} diff --git a/source/README.md b/source/README.md new file mode 100644 index 0000000..ce59b9f --- /dev/null +++ b/source/README.md @@ -0,0 +1,38 @@ +Spring Cloud Stream Source Sample +============================= + +In this *Spring Cloud Stream* sample, a timestamp is published on an interval determined by the fixedDelay property. + +## Requirements + +To run this sample, you will need to have installed: + +* Java 8 or Above + +This example requires Redis to be running on localhost. + +## Code Tour + +This sample is a Spring Boot application that uses Spring Cloud Stream to publish timestamp data. The source module has 3 primary components: + +* SourceApplication - the Spring Boot Main Application +* TimeSource - the module that will generate the timestamp and post the message to the stream +* TimeSourceOptionsMetadata - defines the configurations that are available to setup the TimeSource + * format - how to render the current time, using SimpleDateFormat + * fixedDelay - time delay between messages + * initialDelay - delay before the first message + * timeUnit - the time unit for the fixed and initial delays + * maxMessages - the maximum messages per poll; -1 for unlimited + +## Building with Maven + +Build the sample by executing: + + source>$ mvn clean package + +## Running the Sample + +To start the source module execute the following: + + source>$ java -jar target/spring-cloud-stream-sample-source-1.0.0.BUILD-SNAPSHOT-exec.jar + diff --git a/source/pom.xml b/source/pom.xml new file mode 100644 index 0000000..504c227 --- /dev/null +++ b/source/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + spring-cloud-stream-sample-source + jar + spring-cloud-stream-sample-source + Demo project for source module + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + demo.SourceApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/source/src/main/java/demo/DateFormat.java b/source/src/main/java/demo/DateFormat.java new file mode 100644 index 0000000..17e134c --- /dev/null +++ b/source/src/main/java/demo/DateFormat.java @@ -0,0 +1,93 @@ +/* + * Copyright 2015 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 demo; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.text.SimpleDateFormat; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; + +/** + * The annotated String must be a valid {@link java.text.SimpleDateFormat} pattern. + * + * @author Eric Bottard + */ +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {DateFormat.DateFormatValidator.class}) +public @interface DateFormat { + + String DEFAULT_MESSAGE = ""; + + String message() default DEFAULT_MESSAGE; + + Class[] groups() default {}; + + Class[] payload() default {}; + + + /** + * Defines several {@link DateFormat} annotations on the same element. + * + * @see DateFormat + */ + @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER}) + @Retention(RUNTIME) + @Documented + @interface List { + + DateFormat[] value(); + } + + public static class DateFormatValidator implements ConstraintValidator { + + private String message; + + @Override + public void initialize(DateFormat constraintAnnotation) { + this.message = constraintAnnotation.message(); + } + + @Override + public boolean isValid(CharSequence value, ConstraintValidatorContext context) { + if (value == null) { + return true; + } + try { + new SimpleDateFormat(value.toString()); + } + catch (IllegalArgumentException e) { + if (DEFAULT_MESSAGE.equals(this.message)) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation(); + } + return false; + } + return true; + } + } + +} diff --git a/source/src/main/java/demo/SourceApplication.java b/source/src/main/java/demo/SourceApplication.java new file mode 100644 index 0000000..2770520 --- /dev/null +++ b/source/src/main/java/demo/SourceApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 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 demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SourceApplication { + + public static void main(String[] args) { + SpringApplication.run(SourceApplication.class, args); + } + +} diff --git a/source/src/main/java/demo/TimeSource.java b/source/src/main/java/demo/TimeSource.java new file mode 100644 index 0000000..77d909a --- /dev/null +++ b/source/src/main/java/demo/TimeSource.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015 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 demo; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Source; +import org.springframework.context.annotation.Bean; +import org.springframework.integration.annotation.InboundChannelAdapter; +import org.springframework.integration.annotation.Poller; +import org.springframework.integration.core.MessageSource; +import org.springframework.messaging.support.GenericMessage; + +/** + * @author Dave Syer + * @author Glenn Renfro + * + */ +@EnableBinding(Source.class) +@EnableConfigurationProperties(TimeSourceOptionsMetadata.class) +public class TimeSource { + + @Autowired + private TimeSourceOptionsMetadata options; + + @Bean + @InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "${fixedDelay}", maxMessagesPerPoll = "1")) + public MessageSource timerMessageSource() { + return () -> new GenericMessage<>(new SimpleDateFormat(this.options.getFormat()).format(new Date())); + } + +} diff --git a/source/src/main/java/demo/TimeSourceOptionsMetadata.java b/source/src/main/java/demo/TimeSourceOptionsMetadata.java new file mode 100644 index 0000000..d88fda6 --- /dev/null +++ b/source/src/main/java/demo/TimeSourceOptionsMetadata.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013-2015 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 demo; + +import javax.validation.constraints.Min; +import javax.validation.constraints.Pattern; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Describes options to the {@code time} source module. + * + * @author Eric Bottard + * @author Gary Russell + */ +@ConfigurationProperties +public class TimeSourceOptionsMetadata { + + /** + * how to render the current time, using SimpleDateFormat + */ + private String format = "yyyy-MM-dd HH:mm:ss"; + + /** + * time delay between messages, expressed in TimeUnits (seconds by default) + */ + private int fixedDelay = 1; + + /** + * an initial delay when using a fixed delay trigger, expressed in TimeUnits (seconds by default) + */ + private int initialDelay = 0; + + /** + * the time unit for the fixed and initial delays + */ + private String timeUnit = "SECONDS"; + + /** + * the maximum messages per poll; -1 for unlimited + */ + long maxMessages = 1; + + public long getMaxMessages() { + return this.maxMessages; + } + + public void setMaxMessages(long maxMessages) { + this.maxMessages = maxMessages; + } + + @Min(0) + public int getInitialDelay() { + return this.initialDelay; + } + + public void setInitialDelay(int initialDelay) { + this.initialDelay = initialDelay; + } + + @Pattern(regexp = "(?i)(NANOSECONDS|MICROSECONDS|MILLISECONDS|SECONDS|MINUTES|HOURS|DAYS)", + message = "timeUnit must be one of NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS (case-insensitive)") + public String getTimeUnit() { + return this.timeUnit; + } + + public void setTimeUnit(String timeUnit) { + this.timeUnit = timeUnit.toUpperCase(); + } + + @DateFormat + public String getFormat() { + return this.format; + } + + public void setFormat(String format) { + this.format = format; + } + + public int getFixedDelay() { + return this.fixedDelay; + } + + public void setFixedDelay(int fixedDelay) { + this.fixedDelay = fixedDelay; + } + + +} diff --git a/source/src/main/resources/application.yml b/source/src/main/resources/application.yml new file mode 100644 index 0000000..72f476d --- /dev/null +++ b/source/src/main/resources/application.yml @@ -0,0 +1,24 @@ +server: + port: 8080 +fixedDelay: 5000 +spring: + cloud: + stream: + bindings: + output: + destination: testtock + contentType: text/plain + # uncomment below to use the last digit of the seconds as a partition key + # hashcode(key) % N is then applied with N being the partitionCount value + # thus, even seconds should go to the 0 queue, odd seconds to the 1 queue + #producerProperties: + # partitionKeyExpression: payload.charAt(payload.length()-1) + # partitionCount: 2 + +--- +spring: + profiles: extended + cloud: + stream: + bindings: + output: xformed diff --git a/source/src/test/java/demo/ModuleApplicationTests.java b/source/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..ae245b5 --- /dev/null +++ b/source/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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 demo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SourceApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/transform/pom.xml b/transform/pom.xml new file mode 100644 index 0000000..a0bd1d6 --- /dev/null +++ b/transform/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + spring-cloud-stream-sample-transform + jar + + spring-cloud-stream-sample-transform + Demo project for transform module + + + org.springframework.cloud + spring-cloud-stream-samples + 1.0.0.BUILD-SNAPSHOT + + + + demo.TransformApplication + + + + + org.springframework.cloud + spring-cloud-stream + + + org.springframework.cloud + spring-cloud-stream-binder-redis + + + org.springframework.boot + spring-boot-starter-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + + + diff --git a/transform/src/main/java/demo/LoggingTransformer.java b/transform/src/main/java/demo/LoggingTransformer.java new file mode 100644 index 0000000..9bcfdc5 --- /dev/null +++ b/transform/src/main/java/demo/LoggingTransformer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015 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 demo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.messaging.Processor; +import org.springframework.integration.annotation.ServiceActivator; + +/** + * @author Dave Syer + * + */ +@EnableBinding(Processor.class) +@ConfigurationProperties("module.logging") +public class LoggingTransformer { + + private static Logger logger = LoggerFactory.getLogger(LoggingTransformer.class); + + /** + * The name to include in the log message + */ + private String name = "logging"; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @ServiceActivator(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT) + public Object transform(Object payload) { + logger.info("Transformed by " + this.name + ": " + payload); + return payload; + } + +} diff --git a/transform/src/main/java/demo/TransformApplication.java b/transform/src/main/java/demo/TransformApplication.java new file mode 100644 index 0000000..48e007b --- /dev/null +++ b/transform/src/main/java/demo/TransformApplication.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 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 demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TransformApplication { + + public static void main(String[] args) { + SpringApplication.run(TransformApplication.class, args); + } + +} diff --git a/transform/src/main/resources/application.yml b/transform/src/main/resources/application.yml new file mode 100644 index 0000000..f62709e --- /dev/null +++ b/transform/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + port: 8082 +spring: + cloud: + stream: + bindings: + output: xformed + input: testtock + \ No newline at end of file diff --git a/transform/src/test/java/demo/ModuleApplicationTests.java b/transform/src/test/java/demo/ModuleApplicationTests.java new file mode 100644 index 0000000..c1ee7d6 --- /dev/null +++ b/transform/src/test/java/demo/ModuleApplicationTests.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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 demo; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = TransformApplication.class) +@WebAppConfiguration +@DirtiesContext +public class ModuleApplicationTests { + + @Test + public void contextLoads() { + } + +}