-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WildFly mini series: REST Client #671
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,17 @@ | ||
:simple-microservice-part1: WildFly Java Microservice - PART 1: Docker Image | ||
:simple-microservice-header: WildFly Java Microservice | ||
:simple-microservice-part1: WildFly Java Microservice - PART 1: Container Image | ||
:simple-microservice-part2: WildFly Java Microservice - PART 2: Kubernetes | ||
:simple-microservice-database-part1: Connecting to a DB - PART 1: Docker Image | ||
:simple-microservice-database-header: Connecting to a DB | ||
:simple-microservice-database-part1: Connecting to a DB - PART 1: Container Image | ||
:simple-microservice-database-part2: Connecting to a DB - PART 2: Kubernetes | ||
:simple-microservice-jms-part1: Using a Message Broker - PART 1: Docker Image | ||
:simple-microservice-jms-header: Using a Message Broker | ||
:simple-microservice-jms-part1: Using a Message Broker - PART 1: Container Image | ||
:simple-microservice-jms-part2: Using a Message Broker - PART 2: Kubernetes | ||
:simple-microservice-infinispan-part1: Using Infinispan remote cache - PART 1: Docker Image | ||
:simple-microservice-infinispan-header: Using Infinispan remote cache | ||
:simple-microservice-infinispan-part1: Using Infinispan remote cache - PART 1: Container Image | ||
:simple-microservice-infinispan-part2: Using Infinispan remote cache - PART 2: Kubernetes | ||
:simple-microservice-client-header: Invoke one Microservices from another | ||
:simple-microservice-client-part1: Invoke one Microservices from another - PART 1: Container Images | ||
:simple-microservice-client-part2: Invoke one Microservices from another - PART 2: Kubernetes | ||
:simple-microservice-client-part3: Invoke one Microservices from another - PART 3: Propagate Authentication | ||
:get-enterprise-ready: Get “Enterprise” ready |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,296 @@ | ||
= {simple-microservice-client-part1} | ||
:summary: Invoke one microservice from another | ||
:includedir: ../_includes | ||
include::{includedir}/_attributes.adoc[] | ||
include::./_includes/_titles.adoc[] | ||
include::_includes/_constants.adoc[] | ||
// you can override any attributes eg to lengthen the | ||
// time to complete the guide | ||
:prerequisites-time: 10 | ||
|
||
In this guide, you will learn HOW-TO invoke one microservice from another; | ||
|
||
[[prerequisites]] | ||
== Prerequisites | ||
|
||
To complete this guide, you need: | ||
|
||
* Complete link:simple-microservice-part1[{simple-microservice-part1}] | ||
|
||
== Introduction | ||
|
||
This guide is the first in a series of three: | ||
|
||
1. In link:simple-microservice-client-part1[{simple-microservice-client-part1}] (this guide), we explain HOW-TO invoke one microservice from another; | ||
2. In link:simple-microservice-client-part2[{simple-microservice-client-part2}], as usual, we explain HOW-TO run the whole thing on Kubernetes | ||
3. In link:simple-microservice-client-part3[{simple-microservice-client-part3}] we explain HOW-TO propagate user authentication and authorization data from the calling microservice to the invoked microservice; this is most useful in a scenario where you have a "chain" of microservices ("**A -> B -> C -> etc.**") and you want the user's authentication and authorization data to be propagated from one microservice to the next; | ||
|
||
== This Guide | ||
|
||
In these guides, we work with a simple invocation chain composed by +++<u>two</u>+++ microservices: | ||
|
||
* **Microservice A**: acting as **client** | ||
* **Microservice B**: acting as **server** | ||
|
||
Our invocation chain is then: "**Microservice A -> Microservice B**": when working with https://microprofile.io/[Microprofile], this is achieved by using the https://github.com/eclipse/microprofile-rest-client[microprofile-rest-client]; | ||
|
||
Specifically, **Microservice A** will use the https://github.com/eclipse/microprofile-rest-client[microprofile-rest-client] to invoke the Jakarta REST service exposed by **Microservice B**; | ||
|
||
For both services, we will start from the microservice we built in link:simple-microservice-part1[{simple-microservice-part1}] (complete code in {source-code-git-repository}/simple-microservice); | ||
|
||
== Microservice B - the server | ||
|
||
We start from the server because we need the server's API for the client later on; | ||
|
||
=== Maven Project | ||
|
||
Copy {source-code-git-repository}/simple-microservice into a new folder named *simple-microservice-server* and: | ||
|
||
* remove folder *src/test* | ||
* remove all test scope dependencies | ||
|
||
NOTE: we remove tests because, since we are going to introduce service to service invocation, they wouldn't be much useful anymore | ||
|
||
==== pom.xml | ||
|
||
Update the `artifactId` to `<artifactId>simple-microservice-server</artifactId>`; | ||
|
||
NOTE: **Microservice B** is basically unchanged, we will modify it in link:simple-microservice-client-part3[{simple-microservice-client-part3}] | ||
|
||
==== Build the application | ||
|
||
[source,bash] | ||
---- | ||
mvn clean package | ||
---- | ||
|
||
=== Docker Image | ||
|
||
==== Dockerfile | ||
|
||
Since you copied {source-code-git-repository}/simple-microservice[simple-microservice], the Dockerfile from link:https://github.com/wildfly/wildfly-s2i/blob/main/examples/docker-build/Dockerfile[examples/docker-build/Dockerfile, window="_blank"] should already be at the root of your project; | ||
|
||
==== Build the Docker Image | ||
|
||
[source,bash,subs="normal"] | ||
---- | ||
podman build -t {simple-microservice-server}:latest . | ||
---- | ||
|
||
NOTE: You can use link:https://docs.wildfly.org/wildfly-maven-plugin/releases/{version-wildfly-maven-plugin-docs}/image-mojo.html[`wildfly-maven-plugin`, window="_blank"] to automate the image build | ||
|
||
==== Run the Docker Image | ||
|
||
First we create a network for our containers: | ||
|
||
[source,bash,subs="normal"] | ||
---- | ||
podman network create {podman-network-name} | ||
---- | ||
|
||
Then we run our container using this network: | ||
|
||
[source,bash,subs="normal"] | ||
---- | ||
podman run --rm -p 8180:8080 -p 10090:9990 \ | ||
--network={podman-network-name} \ | ||
--name={simple-microservice-server} \ | ||
{simple-microservice-server} | ||
---- | ||
|
||
== Microservice A - the client | ||
|
||
=== Maven Project | ||
|
||
Copy {source-code-git-repository}/simple-microservice into a new folder named *simple-microservice-client* and: | ||
|
||
* remove folder *src/test* | ||
* remove all test scope dependencies | ||
|
||
NOTE: we remove tests because, since we are going to introduce service to service invocation, they wouldn't be much useful anymore | ||
|
||
==== pom.xml | ||
|
||
Update the artifactId to `<artifactId>simple-microservice-client</artifactId>`; | ||
|
||
Add the following to `dependencyManagement`: | ||
|
||
[source,xml,subs="normal"] | ||
---- | ||
<dependency> | ||
<groupId>org.wildfly.bom</groupId> | ||
<artifactId>wildfly-expansion</artifactId> | ||
<version>${version.wildfly.bom}</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
---- | ||
|
||
Add the following to `dependencies`: | ||
|
||
[source,xml,subs="normal"] | ||
---- | ||
<dependency> | ||
<groupId>org.eclipse.microprofile.rest.client</groupId> | ||
<artifactId>microprofile-rest-client-api</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.eclipse.microprofile.config</groupId> | ||
<artifactId>microprofile-config-api</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
---- | ||
|
||
Add the following `layers` in the `wildfly-maven-plugin`: | ||
|
||
[source,xml,subs="normal"] | ||
---- | ||
<layer>microprofile-config</layer> | ||
<layer>microprofile-rest-client</layer> | ||
---- | ||
|
||
Later on, we will use: | ||
|
||
* **microprofile-config** to make the URL to **Microservice B** configurable | ||
* **microprofile-rest-client** to actually invoke **Microservice B** | ||
|
||
==== microprofile-config.properties | ||
|
||
As anticipated, we use **microprofile-config** to make the URL to **Microservice B** configurable; | ||
|
||
Add file `src/main/resources/META-INF/microprofile-config.properties` with the following content: | ||
|
||
.microprofile-config.properties: | ||
[source,properties] | ||
---- | ||
simple-microservice-server/mp-rest/uri=${simple-microservice-server-uri:http://127.0.0.1:8080} | ||
simple-microservice-server/mp-rest/connectTimeout=3000 | ||
---- | ||
|
||
NOTE: `simple-microservice-server-uri` would pick up its value, whenever set, from the environment variable named `SIMPLE_MICROSERVICE_SERVER_URI` (see https://download.eclipse.org/microprofile/microprofile-config-3.0/microprofile-config-spec-3.0.html#default_configsources.env.mapping[env.mapping]) | ||
|
||
==== Java code | ||
|
||
Add the following interface: | ||
|
||
.GettingStartedEndpointClient.java: | ||
[source,java] | ||
---- | ||
package org.wildfly.examples; | ||
|
||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.PathParam; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.core.MediaType; | ||
import jakarta.ws.rs.core.Response; | ||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; | ||
|
||
@RegisterRestClient(configKey="simple-microservice-server") | ||
@Path("/hello") | ||
public interface GettingStartedEndpointClient { | ||
@GET | ||
@Path("/{name}") | ||
@Produces(MediaType.TEXT_PLAIN) | ||
Response sayHello(@PathParam("name") String name); | ||
} | ||
---- | ||
|
||
NOTE: this class is used to define the API to be invoked by the Rest Client; the actual URL where the remote service is | ||
located, comes from the `microprofile-config.properties` file we just added; | ||
|
||
Remove the `src/main/java/org/wildfly/examples/GettingStartedService.java` file and replace the content of | ||
`src/main/java/org/wildfly/examples/GettingStartedEndpoint.java` with the following: | ||
Comment on lines
+203
to
+204
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a general question, should we be using the archetype instead and creating a new project if we need to keep deleting files? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was to give the user the feeling that we were simply adapting the same project he's been working on in previous posts ... I figured that would result in a better understanding on the reader side of the modifications required for service to service invocation ... |
||
|
||
.GettingStartedEndpoint.java: | ||
[source,java] | ||
---- | ||
package org.wildfly.examples; | ||
|
||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import jakarta.ws.rs.PathParam; | ||
import jakarta.ws.rs.Produces; | ||
import jakarta.ws.rs.core.MediaType; | ||
import jakarta.ws.rs.core.Response; | ||
import org.eclipse.microprofile.rest.client.inject.RestClient; | ||
|
||
@Path("/") | ||
public class GettingStartedEndpoint { | ||
|
||
@Inject | ||
@RestClient | ||
private GettingStartedEndpointClient client; | ||
|
||
@GET | ||
@Path("/{name}") | ||
@Produces(MediaType.TEXT_PLAIN) | ||
public Response sayHello(final @PathParam("name") String name) { | ||
return client.sayHello(name); | ||
} | ||
} | ||
---- | ||
|
||
NOTE: as anticipated, we use **microprofile-rest-client** to actually invoke **Microservice B** | ||
|
||
==== Build the application | ||
|
||
[source,bash] | ||
---- | ||
mvn clean package | ||
---- | ||
|
||
=== Docker Image | ||
|
||
==== Dockerfile | ||
|
||
Since you copied {source-code-git-repository}/simple-microservice[simple-microservice], the Dockerfile from link:https://github.com/wildfly/wildfly-s2i/blob/main/examples/docker-build/Dockerfile[examples/docker-build/Dockerfile, window="_blank"] should already be at the root of your project; | ||
|
||
==== Build the Docker Image | ||
|
||
Build the Docker Image `{simple-microservice-client}:latest` with the following command: | ||
|
||
[source,bash,subs="normal"] | ||
---- | ||
podman build -t {simple-microservice-client}:latest . | ||
---- | ||
|
||
NOTE: You can use link:https://docs.wildfly.org/wildfly-maven-plugin/releases/{version-wildfly-maven-plugin-docs}/image-mojo.html[`wildfly-maven-plugin`, window="_blank"] to automate the image build | ||
|
||
==== Run the Docker Image | ||
|
||
[source,bash,subs="normal"] | ||
---- | ||
podman run --rm -p 8080:8080 -p 9990:9990 \ | ||
--network={podman-network-name} \ | ||
--env "SIMPLE_MICROSERVICE_SERVER_URI=http://{simple-microservice-server}:8080" \ | ||
--name={simple-microservice-client} \ | ||
{simple-microservice-client} | ||
---- | ||
|
||
NOTE: The **{simple-microservice-server}** container can be reached, inside the **{podman-network-name}** network, using the *DNS* name **{simple-microservice-server}** | ||
|
||
== Test | ||
|
||
Open http://localhost:8080[http://localhost:8080] in your browser: this web page is served by the **{simple-microservice-client}** container; | ||
|
||
Write something in the "Name" input box and then press "Say Hello": the response you'll see will come from **{simple-microservice-server}** container! | ||
|
||
The complete invocation chain is "**web browser** -> **{simple-microservice-client}** -> **{simple-microservice-server}**" | ||
|
||
== What's next? | ||
|
||
link:simple-microservice-client-part2[{simple-microservice-client-part2}] | ||
|
||
[[references]] | ||
== References | ||
|
||
* https://microprofile.io/specifications/microprofile-rest-client[microprofile-rest-client] | ||
* https://microprofile.io/specifications/microprofile-config[microprofile-config] | ||
* Source code for this guide: | ||
** {source-code-git-repository}/simple-microservice-rest-client/simple-microservice-client | ||
** {source-code-git-repository}/simple-microservice-rest-client/simple-microservice-server | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we doing this? Should the tests just be removed from the example if we remove them here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is about service ti service invocation and, unless we start both services in the same test, it would fail... perhaps we could leave the tests in the "server" service ...