From f80d903cfffe50a3b8957bb26c391908caca2345 Mon Sep 17 00:00:00 2001 From: adkm Date: Wed, 4 Oct 2017 17:04:32 +0200 Subject: [PATCH 1/5] Wire Tap EIP implementation and tests --- eip-wire-tap/pom.xml | 63 +++++++++++++++++++ .../java/com/iluwatar/eip/wiretap/App.java | 28 +++++++++ .../eip/wiretap/routes/WireTapRoute.java | 32 ++++++++++ .../src/main/resources/application.properties | 3 + .../eip/wiretap/WireTapRouteTest.java | 62 ++++++++++++++++++ .../resources/application-test.properties | 3 + pom.xml | 3 +- 7 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 eip-wire-tap/pom.xml create mode 100644 eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java create mode 100644 eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java create mode 100644 eip-wire-tap/src/main/resources/application.properties create mode 100644 eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java create mode 100644 eip-wire-tap/src/test/resources/application-test.properties diff --git a/eip-wire-tap/pom.xml b/eip-wire-tap/pom.xml new file mode 100644 index 000000000..b3bc678b0 --- /dev/null +++ b/eip-wire-tap/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + eip-wire-tap + + com.iluwatar + java-design-patterns + 1.18.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.apache.camel + camel-core + ${camel.version} + + + + org.apache.camel + camel-spring-boot + ${camel.version} + + + + + org.springframework.boot + spring-boot-starter-test + + + + org.apache.camel + camel-test-spring + ${camel.version} + + + + \ No newline at end of file diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java new file mode 100644 index 000000000..ab3c280de --- /dev/null +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java @@ -0,0 +1,28 @@ +package com.iluwatar.eip.wiretap; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * In most integration cases there is a need to monitor the messages flowing through the system. It is usually achieved + * by intercepting the message and redirecting it to a different location like console, filesystem or the database. + * It is important that such functionality should not modify the original message and influence the processing path. + * + *

+ * Wire Tap allows you to route messages to a separate location while they are being forwarded to the ultimate + * destination. It basically consumes messages of the input channel and publishes the unmodified message to both + * output channels. + *

+ */ +@SpringBootApplication +public class App { + + /** + * Program entry point. It starts Spring Boot application and using Apache Camel it auto-configures routes. + * + * @param args command line args + */ + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java new file mode 100644 index 000000000..a01b1892c --- /dev/null +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java @@ -0,0 +1,32 @@ +package com.iluwatar.eip.wiretap.routes; + +import org.apache.camel.builder.RouteBuilder; +import org.springframework.stereotype.Component; + +/** + * Sample wire tap route definition. + * + *

+ * It consumes messages out of the direct:entry entry point and forwards them to direct:endpoint. + * Wire Tap intercepts the message and sends it to direct:wireTap, which in turn forwards it to + * direct:wireTapEndpoint. + *

+ * + * In this example input/output endpoints names are stored in application.properties file. + */ +@Component +public class WireTapRoute extends RouteBuilder { + + /** + * Configures the route + * @throws Exception in case of exception during configuration + */ + @Override + public void configure() throws Exception { + // Main route + from("{{entry}}").wireTap("direct:wireTap").to("{{endpoint}}"); + + // Wire tap route + from("direct:wireTap").to("{{wireTapEndpoint}}"); + } +} diff --git a/eip-wire-tap/src/main/resources/application.properties b/eip-wire-tap/src/main/resources/application.properties new file mode 100644 index 000000000..6dabe6ccc --- /dev/null +++ b/eip-wire-tap/src/main/resources/application.properties @@ -0,0 +1,3 @@ +entry=direct:entry +endpoint=direct:endpoint +wireTapEndpoint=direct:wireTapEndpoint \ No newline at end of file diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java new file mode 100644 index 000000000..93e6dda2a --- /dev/null +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java @@ -0,0 +1,62 @@ +package com.iluwatar.eip.wiretap; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Message; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.assertEquals; + +/** + * Test class for WireTapRoute. + *

+ * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need to substitute + * original endpoint names to mocks. + *

+ */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = WireTapRouteTest.class) +@ActiveProfiles("test") +@EnableAutoConfiguration +@ComponentScan +public class WireTapRouteTest { + + @EndpointInject(uri = "{{entry}}") + private ProducerTemplate entry; + + @EndpointInject(uri = "{{endpoint}}") + private MockEndpoint endpoint; + + @EndpointInject(uri = "{{wireTapEndpoint}}") + private MockEndpoint wireTapEndpoint; + + /** + * Test if both endpoints receive exactly one message containing the same, unchanged body. + * @throws Exception in case of en exception during the test + */ + @Test + @DirtiesContext + public void testWireTap() throws Exception { + entry.sendBody("TEST"); + + endpoint.expectedMessageCount(1); + wireTapEndpoint.expectedMessageCount(1); + + endpoint.assertIsSatisfied(); + wireTapEndpoint.assertIsSatisfied(); + + Message endpointIn = endpoint.getExchanges().get(0).getIn(); + Message wireTapEndpointIn = wireTapEndpoint.getExchanges().get(0).getIn(); + + assertEquals("TEST", endpointIn.getBody()); + assertEquals("TEST", wireTapEndpointIn.getBody()); + } +} diff --git a/eip-wire-tap/src/test/resources/application-test.properties b/eip-wire-tap/src/test/resources/application-test.properties new file mode 100644 index 000000000..1719c8ca4 --- /dev/null +++ b/eip-wire-tap/src/test/resources/application-test.properties @@ -0,0 +1,3 @@ +entry=direct:entry +endpoint=mock:endpoint +wireTapEndpoint=mock:wireTapEndpoint \ No newline at end of file diff --git a/pom.xml b/pom.xml index ec39a6914..774b14019 100644 --- a/pom.xml +++ b/pom.xml @@ -145,8 +145,9 @@ cqrs event-sourcing data-transfer-object - throttling + throttling partial-response + eip-wire-tap From 522721559bcb2d106cab572c5a0d15db9828ffc3 Mon Sep 17 00:00:00 2001 From: adkm Date: Wed, 4 Oct 2017 17:20:29 +0200 Subject: [PATCH 2/5] #176 pattern description and diagram --- eip-wire-tap/README.md | 29 +++++++++++++++++++++++++++++ eip-wire-tap/etc/wiretap.gif | Bin 0 -> 3067 bytes 2 files changed, 29 insertions(+) create mode 100644 eip-wire-tap/README.md create mode 100644 eip-wire-tap/etc/wiretap.gif diff --git a/eip-wire-tap/README.md b/eip-wire-tap/README.md new file mode 100644 index 000000000..d2742aa31 --- /dev/null +++ b/eip-wire-tap/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: EIP Wire Tap +folder: eip-wire-tap +permalink: /patterns/eip-wire-tap/ +categories: Enterprise integration +tags: + - Java + - Difficulty-Intermittent + - Enterprise integration +--- + +## Intent +In most integration cases there is a need to monitor the messages flowing through the system. It is usually achieved +by intercepting the message and redirecting it to a different location like console, filesystem or the database. +It is important that such functionality should not modify the original message and influence the processing path. + +![alt text](./etc/wiretap.gif "Wire Tap") + +## Applicability +Use the Wire Tap pattern when + +* You need to monitor messages flowing through the system +* You need to redirect the same, unchanged message to two different endpoints/paths + +## Credits + +* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html) +* [Apache Camel - Documentation](http://camel.apache.org/wire-tap.html) diff --git a/eip-wire-tap/etc/wiretap.gif b/eip-wire-tap/etc/wiretap.gif new file mode 100644 index 0000000000000000000000000000000000000000..4141737167ca7c590baaef807534a74ec6e46c8d GIT binary patch literal 3067 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 Date: Thu, 12 Oct 2017 17:33:03 +0200 Subject: [PATCH 3/5] Added test class for App --- .../java/com/iluwatar/eip/wiretap/AppTest.java | 15 +++++++++++++++ .../wiretap/{ => routes}/WireTapRouteTest.java | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java rename eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/{ => routes}/WireTapRouteTest.java (97%) diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java new file mode 100644 index 000000000..fb254ead2 --- /dev/null +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.eip.wiretap; + +import org.junit.Test; + +/** + * Test for App class + */ +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } +} diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java similarity index 97% rename from eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java rename to eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java index 93e6dda2a..a7b15f45e 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/WireTapRouteTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java @@ -1,4 +1,4 @@ -package com.iluwatar.eip.wiretap; +package com.iluwatar.eip.wiretap.routes; import org.apache.camel.EndpointInject; import org.apache.camel.Message; From 0453ba1315320a267ad182637c6ee5317890d75f Mon Sep 17 00:00:00 2001 From: adkm Date: Fri, 13 Oct 2017 14:02:03 +0200 Subject: [PATCH 4/5] #176 Test run, app shutdown, minor refactoring --- .../java/com/iluwatar/eip/wiretap/App.java | 27 +++++++++++++++++-- .../eip/wiretap/routes/WireTapRoute.java | 2 +- .../com/iluwatar/eip/wiretap/AppTest.java | 2 +- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java index ab3c280de..73ae094fa 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java @@ -1,7 +1,10 @@ package com.iluwatar.eip.wiretap; +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; /** * In most integration cases there is a need to monitor the messages flowing through the system. It is usually achieved @@ -22,7 +25,27 @@ public class App { * * @param args command line args */ - public static void main(String[] args) { - SpringApplication.run(App.class, args); + public static void main(String[] args) throws Exception{ + // Run Spring Boot application and obtain ApplicationContext + ConfigurableApplicationContext context = SpringApplication.run(App.class, args); + + // Get CamelContext from ApplicationContext + CamelContext camelContext = (CamelContext) context.getBean("camelContext"); + + // Add a new routes that will handle endpoints form WireTapRoute class. + camelContext.addRoutes(new RouteBuilder() { + + @Override + public void configure() throws Exception { + from("{{endpoint}}").log("ENDPOINT: ${body}"); + from("{{wireTapEndpoint}}").log("WIRETAPPED ENDPOINT: ${body}"); + } + + }); + + // Add producer that will send test message to an entry point in WireTapRoute + camelContext.createProducerTemplate().sendBody("{{entry}}", "Test message"); + + SpringApplication.exit(context); } } diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java index a01b1892c..c744295ee 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java @@ -27,6 +27,6 @@ public class WireTapRoute extends RouteBuilder { from("{{entry}}").wireTap("direct:wireTap").to("{{endpoint}}"); // Wire tap route - from("direct:wireTap").to("{{wireTapEndpoint}}"); + from("direct:wireTap").log("Message: ${body}").to("{{wireTapEndpoint}}"); } } diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java index fb254ead2..25b9d8550 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java @@ -8,7 +8,7 @@ import org.junit.Test; public class AppTest { @Test - public void testMain() { + public void testMain() throws Exception { String[] args = {}; App.main(args); } From 624a4ef8574b2947d10a5390e3d1a11869037b78 Mon Sep 17 00:00:00 2001 From: adkm Date: Fri, 13 Oct 2017 14:22:48 +0200 Subject: [PATCH 5/5] #176 Fixed CheckStyle issue --- eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java index 73ae094fa..7bf4ce0d4 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java @@ -25,7 +25,7 @@ public class App { * * @param args command line args */ - public static void main(String[] args) throws Exception{ + public static void main(String[] args) throws Exception { // Run Spring Boot application and obtain ApplicationContext ConfigurableApplicationContext context = SpringApplication.run(App.class, args);