From 7afb60674488815dd3295ae1d27405fc5249cb63 Mon Sep 17 00:00:00 2001 From: Dileep Kumar T Date: Sun, 26 Jul 2026 23:56:24 -0400 Subject: [PATCH] feat: implement Remote Procedure Invocation pattern (#2680) - Add RPI pattern demonstrating REST-based synchronous communication - Implement RpiService (Spring Boot REST controller) - Implement RpiClient (using RestTemplate) - Add comprehensive unit and integration tests - Add UML diagram and README documentation - Resolves #2680 --- pom.xml | 945 +++++++++--------- remote-procedure-invocation/README.md | 126 +++ remote-procedure-invocation/etc/rpi.urm.png | Bin 0 -> 19896 bytes remote-procedure-invocation/etc/rpi.urm.puml | 18 + remote-procedure-invocation/pom.xml | 71 ++ .../src/main/java/com/iluwatar/rpi/App.java | 60 ++ .../main/java/com/iluwatar/rpi/RpiClient.java | 65 ++ .../java/com/iluwatar/rpi/RpiService.java | 49 + .../test/java/com/iluwatar/rpi/AppTest.java | 89 ++ .../java/com/iluwatar/rpi/RpiClientTest.java | 53 + .../java/com/iluwatar/rpi/RpiServiceTest.java | 45 + 11 files changed, 1049 insertions(+), 472 deletions(-) create mode 100644 remote-procedure-invocation/README.md create mode 100644 remote-procedure-invocation/etc/rpi.urm.png create mode 100644 remote-procedure-invocation/etc/rpi.urm.puml create mode 100644 remote-procedure-invocation/pom.xml create mode 100644 remote-procedure-invocation/src/main/java/com/iluwatar/rpi/App.java create mode 100644 remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiClient.java create mode 100644 remote-procedure-invocation/src/main/java/com/iluwatar/rpi/RpiService.java create mode 100644 remote-procedure-invocation/src/test/java/com/iluwatar/rpi/AppTest.java create mode 100644 remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiClientTest.java create mode 100644 remote-procedure-invocation/src/test/java/com/iluwatar/rpi/RpiServiceTest.java 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 0000000000000000000000000000000000000000..c11ca6f2212a36ba486429df663a147a8efbb04f GIT binary patch literal 19896 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 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: + * + *

    + *
  • Synchronous communication — the client blocks until the response arrives. + *
  • Simple and familiar — leverages standard HTTP/REST protocols. + *
  • No intermediate broker — direct client-to-service communication. + *
  • Both client and service must be available at the time of the call (tight runtime coupling). + *
+ * + * @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