|
10 | 10 | :icons: font |
11 | 11 | :source-highlighter: prettify |
12 | 12 | :project_id: gs-integration |
13 | | -This guide walks you through the process of using Spring Integration to create a simple application that retrieves data from an RSS Feed (Spring Blog), manipulates the data, and then writes it to a file. This guide uses traditional Spring Integration XML configuration; other guides exist showing the use of JavaConfig/DSL with and without JDK 8 Lambda expressions. |
14 | 13 |
|
15 | | -== What you'll build |
| 14 | +This guide walks you through the process of using Spring Integration to create a simple |
| 15 | +application that retrieves data from an RSS Feed (Spring Blog), manipulates the data, and |
| 16 | +then writes it to a file. This guide uses traditional Spring Integration XML |
| 17 | +configuration. Other guides show how to use Java Configuration and DSL with and without |
| 18 | +JDK 8 Lambda expressions. |
16 | 19 |
|
17 | | -You'll create a flow with Spring Integration using traditional XML configuration. |
| 20 | +== What You Will Build |
18 | 21 |
|
19 | | -== What you'll need |
| 22 | +You will create a flow with Spring Integration by using traditional XML configuration. |
| 23 | + |
| 24 | +== What You Need |
20 | 25 |
|
21 | 26 | :java_version: 1.8 |
22 | 27 | include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/prereq_editor_jdk_buildtools.adoc[] |
23 | 28 |
|
24 | 29 | include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/how_to_complete_this_guide.adoc[] |
25 | 30 |
|
26 | | -include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-gradle.adoc[] |
| 31 | +[[scratch]] |
| 32 | +== Starting with Spring Initializr |
27 | 33 |
|
28 | | -include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-maven.adoc[] |
| 34 | +For all Spring applications, you should start with the https://start.spring.io[Spring |
| 35 | +Initializr]. The Initializr offers a fast way to pull in all the dependencies you need for |
| 36 | +an application and does a lot of the set up for you. This example needs only the Spring |
| 37 | +Integration dependency. The following image shows the Initializr set up for this sample |
| 38 | +project: |
29 | 39 |
|
30 | | -include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-sts.adoc[] |
| 40 | +image::images/initializr.png[] |
31 | 41 |
|
| 42 | +NOTE: The preceding image shows the Initializr with Maven chosen as the build tool. You |
| 43 | +can also use Gradle. It also shows values of `com.example` and `integration` as the Group |
| 44 | +and Artifact, respectively. You will use those values throughout the rest of this sample. |
32 | 45 |
|
33 | | -[[initial]] |
34 | | -== Define an integration flow |
| 46 | +The following listing shows the `pom.xml` file that is created when you choose Maven: |
| 47 | + |
| 48 | +==== |
| 49 | +[src,xml] |
| 50 | +---- |
| 51 | +include::initial/pom.xml[] |
| 52 | +---- |
| 53 | +==== |
35 | 54 |
|
36 | | -For this guide's sample application, you will define a Spring Integration flow that reads blog posts from Spring IO's RSS feed, transforms them into an easily readable `String` consisting of the post title and the URL for the post, and appends that `String` to the end of a file `/tmp/si/SpringBlog`. |
| 55 | +The following listing shows the `build.gradle` file that is created when you choose Gradle: |
37 | 56 |
|
38 | | -To define an integration flow, you simply create a Spring XML configuration with a handful of elements from Spring Integration's XML namespaces. Specifically, for the desired integration flow, you work with elements from these Spring Integration namespaces: core, feed, and file. |
| 57 | +==== |
| 58 | +[src,java] |
| 59 | +---- |
| 60 | +include::initial/build.gradle[] |
| 61 | +---- |
| 62 | +==== |
39 | 63 |
|
40 | | -The following XML configuration file defines the integration flow: |
| 64 | +== Add to the Build Files |
41 | 65 |
|
42 | | -`src/main/resources/hello/integration.xml` |
43 | | -[source,xml] |
| 66 | +For this example, you need to add two dependencies: |
| 67 | + |
| 68 | +* `spring-integration-feed` |
| 69 | +* `spring-integration-file` |
| 70 | + |
| 71 | +The following listing shows the final `pom.xml` file: |
| 72 | + |
| 73 | +==== |
| 74 | +[src,xml] |
44 | 75 | ---- |
45 | | -include::complete/src/main/resources/hello/integration.xml[] |
| 76 | +include::complete/pom.xml[] |
46 | 77 | ---- |
| 78 | +==== |
47 | 79 |
|
48 | | -As you can see, three integration elements are in play here: |
| 80 | +The following listing shows the final `build.gradle` file: |
49 | 81 |
|
50 | | - * `<feed:inbound-channel-adapter>`. An inbound adapter that retrieves the posts, one per poll. As configured here, it polls every 5 seconds. The posts are placed into a channel named "news" (corresponding with the adapter's ID). |
51 | | - * `<int:transformer>`. Transforms entries (`com.rometools.rome.feed.synd.SyndEntry`) in the "news" channel, extracting the entry's title (`payload.title`) and link (`payload.link`) and concatenating them into a readable `String` (adding a newline). The `String` is then sent to the output channel named "file". |
52 | | - * `<file:outbound-channel-adapter>`. An outbound channel adapter that writes content from its channel (here named "file") to a file. Specifically, as configured here, it will append anything in the "file" channel to a file at `/tmp/si/SpringBlog`. |
| 82 | +==== |
| 83 | +[src,java] |
| 84 | +---- |
| 85 | +include::complete/build.gradle[] |
| 86 | +---- |
| 87 | +==== |
53 | 88 |
|
54 | | -This simple flow is illustrated like this: |
| 89 | +[[initial]] |
| 90 | +== Define an Integration Flow |
55 | 91 |
|
56 | | -image::images/blogToFile.png[A flow that reads RSS feed entries, transforms them to a String, and appends them to a file.] |
| 92 | +For this guide's sample application, you will define a Spring Integration flow that: |
57 | 93 |
|
58 | | -Ignore the `auto-startup` attribute for now; we'll revisit that later when discussing testing; just notice that it will be `true` by default which means the posts will be fetched when the application starts. |
59 | | -Also note the property placeholder in the `filename-generator-expression`; this means the default will be `SpringBlog` but can be overridden with a property |
| 94 | +* Reads blog posts from the RSS feed at spring.io. |
| 95 | +* Transforms them into an easily readable `String` consisting of the post title and the URL for the post. |
| 96 | +* Appends that `String` to the end of a file (`/tmp/si/SpringBlog`). |
60 | 97 |
|
61 | | -== Make the application executable |
| 98 | +To define an integration flow, you can create a Spring XML configuration with a handful of |
| 99 | +elements from Spring Integration's XML namespaces. Specifically, for the desired |
| 100 | +integration flow, you work with elements from these Spring Integration namespaces: core, |
| 101 | +feed, and file. (Getting the last two is why we had to modify the build files provided by |
| 102 | +the Spring Initializr.) |
62 | 103 |
|
63 | | -Although it is common to configure a Spring Integration flow within a larger application, perhaps even a web application, there's no reason that it can't be defined in a simpler standalone application. |
64 | | -That's what you do next, creating a main class that kicks off the integration flow and also declares a handful of beans to support the integration flow. You also build the application into a standalone executable JAR file. |
65 | | -We use Spring Boot's `SpringApplication` to create the application context. |
66 | | -Since this guide uses an the XML namespace for the integration flow, notice that we use `@ImportResource` to load it into the application context. |
| 104 | +The following XML configuration file (from |
| 105 | +`src/main/resources/integration/integration.xml`) defines the integration flow: |
67 | 106 |
|
| 107 | +==== |
| 108 | +[source,xml] |
| 109 | +---- |
| 110 | +include::complete/src/main/resources/integration/integration.xml[] |
| 111 | +---- |
| 112 | +==== |
| 113 | + |
| 114 | +Three integration elements are in play here: |
| 115 | + |
| 116 | +* `<feed:inbound-channel-adapter>`: An inbound adapter that retrieves the posts, one per |
| 117 | +poll. As configured here, it polls every five seconds. The posts are placed into a channel |
| 118 | +named `news` (corresponding to the adapter's ID). |
| 119 | +* `<int:transformer>`: Transforms entries (`com.rometools.rome.feed.synd.SyndEntry`) in |
| 120 | +the `news` channel, extracting the entry's title (`payload.title`) and link |
| 121 | +(`payload.link`) and concatenating them into a readable `String` (and adding a newline). |
| 122 | +The `String` is then sent to the output channel named `file`. |
| 123 | +* `<file:outbound-channel-adapter>`: An outbound channel adapter that writes content from |
| 124 | +its channel (named `file`) to a file. Specifically, as configured here, it appends |
| 125 | +anything in the `file` channel to a file at `/tmp/si/SpringBlog`. |
| 126 | + |
| 127 | +The following image shows this simple flow: |
| 128 | + |
| 129 | +image::images/blogToFile.png[A flow that reads RSS feed entries, transforms them to a String, and appends them to a file.] |
68 | 130 |
|
69 | | -`src/main/java/hello/Application.java` |
| 131 | +Ignore the `auto-startup` attribute for now. We revisit that later when we discuss |
| 132 | +testing. For now, notice that it is, by default, `true`, which means the posts are fetched |
| 133 | +when the application starts. Also note the property placeholder in the |
| 134 | +`filename-generator-expression`. It means that the default is `SpringBlog` but can be |
| 135 | +overridden with a property. |
| 136 | + |
| 137 | +== Make the Application Executable |
| 138 | + |
| 139 | +Although it is common to configure a Spring Integration flow within a larger application |
| 140 | +(perhaps even a web application), there is no reason that it cannot be defined in a |
| 141 | +simpler standalone application. That is what you will do next: Create a main class that |
| 142 | +kicks off the integration flow and that declares a handful of beans to support the |
| 143 | +integration flow. You will also build the application into a standalone executable JAR |
| 144 | +file. We use Spring Boot's `@SpringBootApplication` annotation to create the application |
| 145 | +context. Since this guide uses the XML namespace for the integration flow, you must use |
| 146 | +the `@ImportResource` annotation to load it into the application context. The following |
| 147 | +listing (from `src/main/java/com/example/integration/IntegrationApplication.java`) shows |
| 148 | +the application file: |
| 149 | + |
| 150 | +==== |
70 | 151 | [source,java] |
71 | 152 | ---- |
72 | | -include::complete/src/main/java/hello/Application.java[] |
| 153 | +include::complete/src/main/java/com/example/integration/IntegrationApplication.java[] |
73 | 154 | ---- |
| 155 | +==== |
74 | 156 |
|
75 | 157 | include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/build_an_executable_jar_subhead.adoc[] |
76 | 158 | include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/build_an_executable_jar_with_both.adoc[] |
77 | 159 |
|
78 | 160 | [[run]] |
79 | 161 | == Run the application |
80 | 162 |
|
81 | | -Now you can run the application from the jar: |
82 | | -.... |
| 163 | +Now you can run the application from the jar by running the following command: |
| 164 | + |
| 165 | +==== |
| 166 | +[source,bash] |
| 167 | +---- |
83 | 168 | java -jar build/libs/{project_id}-0.1.0.jar |
84 | 169 |
|
85 | 170 | ... app starts up ... |
86 | | -.... |
| 171 | +---- |
| 172 | +==== |
87 | 173 |
|
88 | | -Once the application starts up, it connects to the RSS feed and starts fetching blog posts. The application processes those posts through the integration flow you defined, ultimately appending the post information to a file at `/tmp/si/SpringBlog`. |
| 174 | +Once the application starts, it connects to the RSS feed and starts fetching blog posts. |
| 175 | +The application processes those posts through the integration flow you defined, ultimately |
| 176 | +appending the post information to a file at `/tmp/si/SpringBlog`. |
89 | 177 |
|
90 | | -After the application has been running for awhile, you should be able to view the file at `/tmp/si/SpringBlog` to see the data from a handful of posts. On a UNIX-based operating system, you can also choose to tail the file to see the results as they are written: |
| 178 | +After the application has been running for awhile, you should be able to view the file at |
| 179 | +`/tmp/si/SpringBlog` to see the data from a handful of posts. On a UNIX-based operating |
| 180 | +system, you can also `tail` the file to see the results, as they are written, by running |
| 181 | +the following command: |
91 | 182 |
|
| 183 | +==== |
| 184 | +[source,bash] |
92 | 185 | ---- |
93 | 186 | tail -f /tmp/si/SpringBlog |
94 | 187 | ---- |
| 188 | +==== |
95 | 189 |
|
96 | | -You should see something like this (the actual news will differ): |
| 190 | +You should see something like the following sample output (though the actual news will |
| 191 | +differ): |
97 | 192 |
|
98 | | -.... |
| 193 | +==== |
| 194 | +[source,bash] |
| 195 | +---- |
99 | 196 | Spring Integration Java DSL 1.0 GA Released @ https://spring.io/blog/2014/11/24/spring-integration-java-dsl-1-0-ga-released |
100 | 197 | This Week in Spring - November 25th, 2014 @ https://spring.io/blog/2014/11/25/this-week-in-spring-november-25th-2014 |
101 | 198 | Spring Integration Java DSL: Line by line tutorial @ https://spring.io/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial |
102 | 199 | Spring for Apache Hadoop 2.1.0.M2 Released @ https://spring.io/blog/2014/11/14/spring-for-apache-hadoop-2-1-0-m2-released |
103 | | -.... |
| 200 | +---- |
| 201 | +==== |
104 | 202 |
|
105 | 203 | == Testing |
106 | 204 |
|
107 | | -Examine the `complete` project and you will see a test case. |
| 205 | +Examine the `complete` project and you will see a test case, in |
| 206 | +`src/test/java/com/example/integration/FlowTests.java`: |
108 | 207 |
|
109 | | -`src/test/java/hello/FlowTests.java` |
| 208 | +==== |
110 | 209 | [source,java] |
111 | 210 | ---- |
112 | | -include::complete/src/test/java/hello/FlowTests.java[] |
| 211 | +include::complete/src/test/java/com/example/integration/FlowTests.java[] |
113 | 212 | ---- |
| 213 | +==== |
114 | 214 |
|
115 | | -This uses Spring Boot's test support to set a property `auto.startup` to `false`. |
116 | | -It is generally not a good idea to rely on a network connection for tests, especially in a CI environment. |
117 | | -So, instead, we prevent the feed adapter from starting and inject a `SyndEntry` into the `news` channel for processing by the rest of the flow. |
118 | | -The test also sets the `feed.file.name` so the test writes to a different file; then: |
| 215 | +This test uses Spring Boot's test support to set a property named `auto.startup` to |
| 216 | +`false`. It is generally not a good idea to rely on a network connection for tests, |
| 217 | +especially in a CI environment. Instead, we prevent the feed adapter from starting and |
| 218 | +inject a `SyndEntry` into the `news` channel for processing by the rest of the flow. The |
| 219 | +test also sets the `feed.file.name` so that the test writes to a different file. Then it: |
119 | 220 |
|
120 | | -- verifies the adapter is stopped |
121 | | -- creates a test `SyndEntry` |
122 | | -- deletes the test output file (if it's present) |
123 | | -- sends the message |
124 | | -- verifies the file exists |
125 | | -- reads the file and verifies that the data is as expected |
| 221 | +- Verifies that the adapter is stopped. |
| 222 | +- Creates a test `SyndEntry`. |
| 223 | +- Deletes the test output file (if it is present). |
| 224 | +- Sends the message. |
| 225 | +- Verifies that the file exists. |
| 226 | +- Reads the file and verifies that the data is as expected. |
126 | 227 |
|
127 | 228 | == Summary |
128 | | -Congratulations! You have developed a simple application that uses Spring Integration to fetch blog posts from spring.io, process them, and write them to a file. |
| 229 | + |
| 230 | +Congratulations! You have developed a simple application that uses Spring Integration to |
| 231 | +fetch blog posts from spring.io, process them, and write them to a file. |
129 | 232 |
|
130 | 233 | == See Also |
131 | 234 |
|
|
0 commit comments