diff --git a/pom.xml b/pom.xml index b856945f3b1e..dcd7fcaccc47 100644 --- a/pom.xml +++ b/pom.xml @@ -25,482 +25,483 @@ THE SOFTWARE. --> - - 4.0.0 - com.iluwatar - java-design-patterns - 1.26.0-SNAPSHOT - pom - 2014-2022 - Java Design Patterns - Java Design Patterns - - - UTF-8 + + 4.0.0 + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + pom + 2014-2022 + Java Design Patterns + Java Design Patterns + + + UTF-8 - - 3.4.5 - 5.11.4 - 5.14.2 - 1.5.34 - 2.0.18 + + 3.4.5 + 5.11.4 + 5.14.2 + 1.5.34 + 2.0.18 - - 0.8.13 - 1.4 - 4.17.0 - 2.11.0 - 6.0.0 - 1.1.0 - 1.18.38 - 5.4.0 - 5.4.0 - 2.3.232 + + 0.8.13 + 1.4 + 4.17.0 + 2.11.0 + 6.0.0 + 1.1.0 + 1.18.38 + 5.4.0 + 5.4.0 + 2.3.232 - - 3.5.2 - 5.0.0 - 3.14.0 + + 3.5.2 + 5.0.0 + 3.14.0 - - 5.1.0.4751 - https://sonarcloud.io - iluwatar - iluwatar_java-design-patterns - ${project.artifactId} - Java Design Patterns - - - abstract-document - abstract-factory - active-object - acyclic-visitor - adapter - ambassador - anti-corruption-layer - arrange-act-assert - async-method-invocation - balking - bloc - bridge - builder - business-delegate - bytecode - caching - callback - chain-of-responsibility - circuit-breaker - clean-architecture - client-session - collecting-parameter - collection-pipeline - combinator - command - command-query-responsibility-segregation - commander - component - composite - composite-entity - composite-view - context-object - converter - curiously-recurring-template-pattern - currying - dao-factory - data-access-object - data-bus - data-locality - data-mapper - data-transfer-object - decorator - delegation - dependency-injection - dirty-flag - domain-model - double-buffer - double-checked-locking - double-dispatch - dynamic-proxy - event-aggregator - event-based-asynchronous - event-driven-architecture - event-queue - event-sourcing - execute-around - extension-objects - facade - factory - factory-kit - factory-method - fanout-fanin - feature-toggle - filterer - fluent-interface - flux - flyweight - front-controller - function-composition - game-loop - gateway - guarded-suspension - half-sync-half-async - health-check - hexagonal-architecture - identity-map - immutable - intercepting-filter - interpreter - iterator - layered-architecture - lazy-loading - leader-election - leader-followers - lockable-object - map-reduce - marker-interface - master-worker - mediator - memento - metadata-mapping - microservices-aggregrator - microservices-api-gateway - microservices-client-side-ui-composition - microservices-distributed-tracing - microservices-idempotent-consumer - microservices-log-aggregation - microservices-self-registration - model-view-controller - model-view-intent - model-view-presenter - model-view-viewmodel - monad - money - monitor - monolithic-architecture - monostate - multiton - mute-idiom - notification - null-object - object-mother - object-pool - observer - optimistic-offline-lock - page-controller - page-object - parameter-object - partial-response - pipeline - poison-pill - polling-publisher - presentation-model - private-class-data - producer-consumer - promise - property - prototype - proxy - publish-subscribe - queue-based-load-leveling - reactor - registry - repository - resource-acquisition-is-initialization - retry - role-object - saga - separated-interface - serialized-entity - serialized-lob - servant - server-session - service-layer - service-locator - service-stub - service-to-worker - session-facade - sharding - single-table-inheritance - singleton - spatial-partition - special-case - specification - state - step-builder - strangler - strategy - subclass-sandbox - table-inheritance - table-module - template-method - templateview - thread-pool-executor - thread-specific-storage - throttling - tolerant-reader - trampoline - transaction-script - twin - type-object - unit-of-work - update-method - value-object - version-number - view-helper - virtual-proxy - visitor - backpressure - actor-model - rate-limiting-pattern - - - - jitpack.io - https://jitpack.io - - - + + 5.1.0.4751 + https://sonarcloud.io + iluwatar + iluwatar_java-design-patterns + ${project.artifactId} + Java Design Patterns + + + abstract-document + abstract-factory + active-object + acyclic-visitor + adapter + ambassador + anti-corruption-layer + arrange-act-assert + async-method-invocation + balking + bloc + bridge + builder + business-delegate + bytecode + caching + callback + chain-of-responsibility + circuit-breaker + clean-architecture + client-session + collecting-parameter + collection-pipeline + combinator + command + command-query-responsibility-segregation + commander + component + composite + composite-entity + composite-view + context-object + converter + curiously-recurring-template-pattern + currying + dao-factory + data-access-object + data-bus + data-locality + data-mapper + data-transfer-object + decorator + delegation + dependency-injection + dirty-flag + domain-model + double-buffer + double-checked-locking + double-dispatch + dynamic-proxy + event-aggregator + event-based-asynchronous + event-driven-architecture + event-queue + event-sourcing + execute-around + extension-objects + facade + factory + factory-kit + factory-method + fanout-fanin + feature-toggle + filterer + fluent-interface + flux + flyweight + front-controller + function-composition + game-loop + gateway + guarded-suspension + half-sync-half-async + health-check + hexagonal-architecture + identity-map + immutable + intercepting-filter + interpreter + iterator + layered-architecture + lazy-loading + leader-election + leader-followers + lockable-object + map-reduce + marker-interface + master-worker + mediator + memento + metadata-mapping + microservices-aggregrator + microservices-api-gateway + microservices-client-side-ui-composition + microservices-distributed-tracing + microservices-idempotent-consumer + microservices-log-aggregation + microservices-self-registration + model-view-controller + model-view-intent + model-view-presenter + model-view-viewmodel + monad + money + monitor + monolithic-architecture + monostate + multiton + mute-idiom + notification + null-object + object-mother + object-pool + observer + optimistic-offline-lock + page-controller + page-object + parameter-object + partial-response + pipeline + poison-pill + polling-publisher + presentation-model + private-class-data + producer-consumer + promise + property + prototype + proxy + publish-subscribe + queue-based-load-leveling + reactor + registry + remote-procedure-invocation + repository + resource-acquisition-is-initialization + retry + role-object + saga + separated-interface + serialized-entity + serialized-lob + servant + server-session + service-layer + service-locator + service-stub + service-to-worker + session-facade + sharding + single-table-inheritance + singleton + spatial-partition + special-case + specification + state + step-builder + strangler + strategy + subclass-sandbox + table-inheritance + table-module + template-method + templateview + thread-pool-executor + thread-specific-storage + throttling + tolerant-reader + trampoline + transaction-script + twin + type-object + unit-of-work + update-method + value-object + version-number + view-helper + virtual-proxy + visitor + backpressure + actor-model + rate-limiting-pattern + + + + jitpack.io + https://jitpack.io + + + + + + org.springframework.boot + spring-boot-starter + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-actuator + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring-boot.version} + + + org.springframework.boot + spring-boot-starter-test + test + ${spring-boot.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.htmlunit + htmlunit + ${htmlunit.version} + + + com.google.code.gson + gson + ${gson.version} + + + com.google.inject + guice + ${guice.version} + + + com.github.stefanbirkner + system-lambda + ${system-lambda.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-migrationsupport + ${junit.version} + test + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.mockito + mockito-core + ${mockito.version} + + + org.mongodb + bson + ${bson.version} + + + org.mongodb + mongodb-driver-legacy + ${mongo.version} + + + com.h2database + h2 + ${h2.version} + + + - - org.springframework.boot - spring-boot-starter - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-actuator - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-test - test - ${spring-boot.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.htmlunit - htmlunit - ${htmlunit.version} - - - com.google.code.gson - gson - ${gson.version} - - - com.google.inject - guice - ${guice.version} - - - com.github.stefanbirkner - system-lambda - ${system-lambda.version} - test - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.version} - test - - - org.junit.jupiter - junit-jupiter-migrationsupport - ${junit.version} - test - - - org.slf4j - slf4j-api - ${slf4j.version} - - - ch.qos.logback - logback-classic - ${logback.version} - - - org.mockito - mockito-core - ${mockito.version} - - - org.mongodb - bson - ${bson.version} - - - org.mongodb - mongodb-driver-legacy - ${mongo.version} - - - com.h2database - h2 - ${h2.version} - + + org.projectlombok + lombok + ${lombok.version} + provided + - - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - 21 - 21 - - - org.projectlombok - lombok - ${lombok.version} - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-assembly-plugin - - - package - - single - - - - jar-with-dependencies - - - ${project.artifactId}-${project.version} - false - - - - - - org.sonarsource.scanner.maven - sonar-maven-plugin - ${sonar-maven-plugin.version} - - - - - - com.mycila - license-maven-plugin - ${license-maven-plugin.version} - - - - - -
com/mycila/maven/plugin/license/templates/MIT.txt
-
- - **/README - src/test/resources/** - src/main/resources/** - checkstyle-suppressions.xml - -
-
- - Ilkka Seppälä - iluwatar@gmail.com - -
- - - install-format - install - - format - - - -
- - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - prepare-agent - - prepare-agent - - - - report - - report - - - - - - com.diffplug.spotless - spotless-maven-plugin - 2.44.4 - - - - check - apply - - - - - - - 1.17.0 - - - - -
-
+ + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 21 + 21 + + + org.projectlombok + lombok + ${lombok.version} + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package + + single + + + + jar-with-dependencies + + + ${project.artifactId}-${project.version} + false + + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${sonar-maven-plugin.version} + + + + + + com.mycila + license-maven-plugin + ${license-maven-plugin.version} + + + + + +
com/mycila/maven/plugin/license/templates/MIT.txt
+
+ + **/README + src/test/resources/** + src/main/resources/** + checkstyle-suppressions.xml + +
+
+ + Ilkka Seppälä + iluwatar@gmail.com + +
+ + + install-format + install + + format + + + +
+ + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + prepare-agent + + prepare-agent + + + + report + + report + + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.44.4 + + + + check + apply + + + + + + + 1.17.0 + + + + +
+
diff --git a/remote-procedure-invocation/README.md b/remote-procedure-invocation/README.md new file mode 100644 index 000000000000..810c8a1e4d26 --- /dev/null +++ b/remote-procedure-invocation/README.md @@ -0,0 +1,126 @@ +--- +title: "Remote Procedure Invocation (RPI) Pattern in Java: Synchronous Communication Between Microservices" +shortTitle: Remote Procedure Invocation +description: "Learn the Remote Procedure Invocation (RPI) pattern in Java for enabling synchronous request/reply communication between microservices using REST and Spring Boot." +category: Integration +language: en +tag: + - Client-server + - Decoupling + - Integration + - Microservices + - Synchronous +--- +## Also Known As + +* Remote Procedure Call (RPC) + +## Intent of Remote Procedure Invocation Pattern + +Enable synchronous communication between microservices using a request/reply protocol such as REST, where a client makes a direct call to a remote service and blocks until the response is received. + +## Detailed Explanation of Remote Procedure Invocation Pattern with Real-World Examples + +Real-world example + +> Consider an e-commerce application where the checkout service needs to verify a customer's shipping address by calling the address-validation service. The checkout service sends an HTTP request with the address details and waits for the validation result before proceeding. This synchronous request/reply interaction is the essence of RPI — direct, blocking communication between two services without an intermediate message broker. + +In plain words + +> Remote Procedure Invocation lets one microservice call another over the network using a request/reply protocol (typically HTTP/REST) as if calling a local method, while both must be available at the time of the call. + +## Programmatic Example of Remote Procedure Invocation Pattern in Java + +This example demonstrates a simple RPI setup using Spring Boot. The `RpiService` exposes a REST endpoint, and the `RpiClient` uses `RestTemplate` to call it synchronously. + +**The Service (Server Side)** + +The `RpiService` is a Spring `@RestController` that exposes a `/greet` endpoint: + +```java +@RestController +public class RpiService { + + @GetMapping("/greet") + public String greet(@RequestParam(defaultValue = "World") String name) { + return "Hello, " + name + "!"; + } +} +``` +**The Client** + +The `RpiClient` uses `RestTemplate` to make a synchronous HTTP GET request to the service: +```java +@Slf4j +public class RpiClient { + + private final RestTemplate restTemplate; + private final String baseUrl; + + public RpiClient(RestTemplate restTemplate, String baseUrl) { + this.restTemplate = restTemplate; + this.baseUrl = baseUrl; + } + + public String greet(String name) { + var url = baseUrl + "/greet?name=" + name; + LOGGER.info("Calling remote service: {}", url); + var response = restTemplate.getForObject(url, String.class); + LOGGER.info("Received response: {}", response); + return response; + } +} +``` +**Running the example** + +Start the application and then make a call: + +```bash +curl http://localhost:8080/greet?name=Java +``` +**Console Output** +```bash +Hello, Java! +``` +## When to Use the Remote Procedure Invocation Pattern in Java + +* When services need synchronous request/reply communication. +* When simplicity is preferred over asynchronous messaging infrastructure. +* When the client needs an immediate response from the service before proceeding. + +## Remote Procedure Invocation Pattern Class Diagram + +![Remote Procedure Invocation](./etc/rpi.urm.png) + +## Real-World Applications of Remote Procedure Invocation Pattern in Java + +* RESTful API calls between microservices using `RestTemplate` or `WebClient`. +* gRPC-based service-to-service communication. +* Any HTTP-based synchronous inter-service call in a microservice architecture. + +## Benefits and Trade-offs of Remote Procedure Invocation Pattern + +Benefits: + +* Simple and familiar programming model — similar to calling a local method. +* No need for intermediate message broker infrastructure. +* Easy to understand, debug, and trace. + +Trade-offs: + +* Tight runtime coupling — both client and service must be available simultaneously. +* Reduced availability — if the service is down, the client call fails. +* Potential for cascading failures without resilience patterns like Circuit Breaker. +* Synchronous blocking can limit throughput under high load. + +## Related Java Design Patterns + +* [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/): Protects clients from cascading failures when a remote service is unavailable. +* [Ambassador](https://java-design-patterns.com/patterns/ambassador/): Can act as a proxy for remote service calls, adding retry, logging, or monitoring. +* [Gateway](https://java-design-patterns.com/patterns/gateway/): Provides a single entry point that routes RPI calls to the appropriate backend service. + +## References and Credits + +* [Microservices Patterns: With examples in Java](https://amzn.to/3UyWD5O) +* [Pattern: Remote Procedure Invocation (microservices.io)](https://microservices.io/patterns/communication-style/rpi.html) +* [Spring Boot Documentation](https://spring.io/projects/spring-boot) \ No newline at end of file diff --git a/remote-procedure-invocation/etc/rpi.urm.png b/remote-procedure-invocation/etc/rpi.urm.png new file mode 100644 index 000000000000..c11ca6f2212a Binary files /dev/null and b/remote-procedure-invocation/etc/rpi.urm.png differ diff --git a/remote-procedure-invocation/etc/rpi.urm.puml b/remote-procedure-invocation/etc/rpi.urm.puml new file mode 100644 index 000000000000..fa32df54bb6c --- /dev/null +++ b/remote-procedure-invocation/etc/rpi.urm.puml @@ -0,0 +1,18 @@ +@startuml +package com.iluwatar.rpi { + class App { + + main(args : String[]) {static} + } + class RpiService { + + greet(name : String) : String + } + class RpiClient { + - restTemplate : RestTemplate + - baseUrl : String + + RpiClient(restTemplate : RestTemplate, baseUrl : String) + + greet(name : String) : String + } +} + +RpiClient ..> RpiService : <> +@enduml \ No newline at end of file diff --git a/remote-procedure-invocation/pom.xml b/remote-procedure-invocation/pom.xml new file mode 100644 index 000000000000..22191d197815 --- /dev/null +++ b/remote-procedure-invocation/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + remote-procedure-invocation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.rpi.App + + + + + + + + + \ No newline at end of file diff --git a/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/App.java b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/App.java new file mode 100644 index 000000000000..2026ca4808f4 --- /dev/null +++ b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/App.java @@ -0,0 +1,60 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.rpi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Remote Procedure Invocation (RPI) is a communication pattern used in microservices architectures + * where a client sends a synchronous request to a service and waits for a response. Unlike + * message-based communication, RPI uses a direct request/reply protocol — typically REST over HTTP. + * + *

In this example, the {@link RpiService} exposes a REST endpoint that returns a greeting + * message. The {@link RpiClient} uses Spring's {@link org.springframework.web.client.RestTemplate} + * to make a synchronous HTTP GET call to the service and retrieve the response. + * + *

Key characteristics of RPI: + * + *

+ * + * @see RPI Pattern + */ +@SpringBootApplication +public class App { + + /** Program entry point. */ + public static void main(String[] args) { + var context = SpringApplication.run(App.class, args); + if (args.length > 0 && "test".equals(args[0])) { + context.close(); + } + } +} \ No newline at end of file diff --git a/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiClient.java b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiClient.java new file mode 100644 index 000000000000..390c98fcddd9 --- /dev/null +++ b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiClient.java @@ -0,0 +1,65 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.rpi; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.client.RestTemplate; + +/** + * The client-side component of the RPI pattern. Uses {@link RestTemplate} to make a synchronous + * HTTP GET request to the remote {@link RpiService} and returns the response. This demonstrates the + * core of RPI — a client blocking on a remote call and waiting for the result. + */ +@Slf4j +public class RpiClient { + + private final RestTemplate restTemplate; + private final String baseUrl; + + /** + * Creates a new RpiClient. + * + * @param restTemplate the RestTemplate used to perform HTTP calls + * @param baseUrl the base URL of the remote service (e.g. "http://localhost:8080") + */ + public RpiClient(RestTemplate restTemplate, String baseUrl) { + this.restTemplate = restTemplate; + this.baseUrl = baseUrl; + } + + /** + * Invokes the remote greeting service with the given name. + * + * @param name the name to send to the remote service + * @return the greeting response from the service + */ + public String greet(String name) { + var url = baseUrl + "/greet?name=" + name; + LOGGER.info("Calling remote service: {}", url); + var response = restTemplate.getForObject(url, String.class); + LOGGER.info("Received response: {}", response); + return response; + } +} \ No newline at end of file diff --git a/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiService.java b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiService.java new file mode 100644 index 000000000000..b7116dce365c --- /dev/null +++ b/remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiService.java @@ -0,0 +1,49 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.rpi; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * The server-side component of the RPI pattern. This REST controller exposes an endpoint that + * accepts a name parameter and returns a greeting string. It represents the service that clients + * invoke remotely using a request/reply protocol. + */ +@RestController +public class RpiService { + + /** + * Returns a greeting message for the given name. + * + * @param name the name to greet + * @return a greeting string + */ + @GetMapping("/greet") + public String greet(@RequestParam(defaultValue = "World") String name) { + return "Hello, " + name + "!"; + } +} \ No newline at end of file diff --git a/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/AppTest.java b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/AppTest.java new file mode 100644 index 000000000000..240992c1d85c --- /dev/null +++ b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/AppTest.java @@ -0,0 +1,89 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.rpi; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; + +/** + * Tests for the Remote Procedure Invocation pattern. + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class AppTest { + + @LocalServerPort + private int port; + + @Autowired + private TestRestTemplate testRestTemplate; + + /** + * Verify that the application starts without exception. + */ + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[] {"test"})); + } + + /** + * Verify that the /greet endpoint returns a greeting with default name. + */ + @Test + void shouldReturnDefaultGreeting() { + var response = + testRestTemplate.getForObject("http://localhost:" + port + "/greet", String.class); + assertEquals("Hello, World!", response); + } + + /** + * Verify that the /greet endpoint returns a greeting with a custom name. + */ + @Test + void shouldReturnCustomGreeting() { + var response = + testRestTemplate.getForObject( + "http://localhost:" + port + "/greet?name=Java", String.class); + assertEquals("Hello, Java!", response); + } + + /** + * Verify that the RpiClient correctly calls the service and receives a response. + */ + @Test + void shouldClientReceiveGreetingFromService() { + var client = + new RpiClient( + testRestTemplate.getRestTemplate(), "http://localhost:" + port); + var result = client.greet("RPI"); + assertEquals("Hello, RPI!", result); + } +} \ No newline at end of file diff --git a/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiClientTest.java b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiClientTest.java new file mode 100644 index 000000000000..cd4efad857c2 --- /dev/null +++ b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiClientTest.java @@ -0,0 +1,53 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.rpi; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.web.client.RestTemplate; + +/** Unit tests for {@link RpiClient} using a mocked {@link RestTemplate}. */ +@ExtendWith(MockitoExtension.class) +class RpiClientTest { + + @Mock private RestTemplate restTemplate; + + @Test + void shouldReturnGreetingFromRemoteService() { + var baseUrl = "http://localhost:8080"; + var client = new RpiClient(restTemplate, baseUrl); + + when(restTemplate.getForObject(baseUrl + "/greet?name=Alice", String.class)) + .thenReturn("Hello, Alice!"); + + var result = client.greet("Alice"); + assertEquals("Hello, Alice!", result); + } +} \ No newline at end of file diff --git a/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiServiceTest.java b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiServiceTest.java new file mode 100644 index 000000000000..4f4e42072640 --- /dev/null +++ b/remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiServiceTest.java @@ -0,0 +1,45 @@ +/* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.rpi; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** Unit tests for {@link RpiService}. */ +class RpiServiceTest { + + private final RpiService service = new RpiService(); + + @Test + void shouldGreetWithGivenName() { + assertEquals("Hello, Alice!", service.greet("Alice")); + } + + @Test + void shouldGreetWithDefaultName() { + assertEquals("Hello, World!", service.greet("World")); + } +} \ No newline at end of file