From d0d501b107e1cb78fb269aebf7b0cd5c4980b77e Mon Sep 17 00:00:00 2001 From: Yeray Borges Date: Wed, 19 Jun 2024 18:24:09 +0100 Subject: [PATCH 1/4] [#577] Guide: Integrating with a PostgreSQL database --- _data/guides.yaml | 6 + .../database-integrating-with-postgresql.adoc | 739 ++++++++++++++++++ 2 files changed, 745 insertions(+) create mode 100644 guides/database-integrating-with-postgresql.adoc diff --git a/_data/guides.yaml b/_data/guides.yaml index 865481e8..6578d844 100644 --- a/_data/guides.yaml +++ b/_data/guides.yaml @@ -55,4 +55,10 @@ categories: - title: Deploying WildFly using Ansible url: /guides/automate-with-ansible description: Learn how to automate WildFly deployments with Ansible. + - category: Datasources + cat-id: datasources + guides: + - title: Integrating with a PostgreSQL database + url: /guides/database-integrating-with-postgresql + description: Learn how to configure a datasource to connect a PostgreSQL database. diff --git a/guides/database-integrating-with-postgresql.adoc b/guides/database-integrating-with-postgresql.adoc new file mode 100644 index 00000000..723105e5 --- /dev/null +++ b/guides/database-integrating-with-postgresql.adoc @@ -0,0 +1,739 @@ += Integrating with a PostgreSQL database +:summary: Learn how to configure a datasource to connect a PostgreSQL database +:includedir: _includes +include::{includedir}/_attributes.adoc[] +:prerequisites-time: 20 +:postgre-sql-user: postgres +:postgre-sql-password: admin +:postgre-sql-port: 5432 +:postgre-sql-host: localhost +:postgre-sql-database: bookstore_db +:postgre-docker-image: docker.io/library/postgres +:postgre-sql-kubernetes-service-name: postgres-service + +In this guide, you will learn how to configure WildFly to connect to a PostgreSQL database. You will create a simple Book Store API application to manage books stored in the database using a https://jakarta.ee/specifications/restful-ws/[Jakarta RESTful Web Services (JAX-RS), window=_blank]. + +include::{includedir}/_prerequisites.adoc[] + +* Docker or any Open Container Initiative engine installed. This guide uses https://podman.io/[Podman, window=_blank]. + +== Database + +=== PostgreSQL + +We will use PostgreSQL as database server in its containerized version: see https://hub.docker.com/_/postgres[PostgreSQL Official Image, window=_blank]. + +Start PostgreSQL database in a container with: + +[source,bash,subs="normal"] +---- +podman run --rm --name bookstore \ + -p {postgre-sql-port}:{postgre-sql-port} \ + -e POSTGRES_PASSWORD={postgre-sql-password} \ + -e POSTGRES_USER={postgre-sql-user} \ + -e POSTGRES_DB={postgre-sql-database} \ + {postgre-docker-image} +---- + +NOTE: we started the container with the `--rm` so it can be disposed automatically when we stop it. + + +== Application + +=== Create a new Maven project + +We are going to use the *WildFly Getting Started Archetype* to create the base structure of our Book Store API. + +Open a new terminal window and create a new project using the WildFly Getting Started Archetype: + +[source,bash,subs="normal"] +---- +mvn archetype:generate \ + -DarchetypeGroupId=org.wildfly.archetype \ + -DarchetypeArtifactId=wildfly-getting-started-archetype \ + -DdefaultClassPrefix=BookStore \ + -DartifactId=bookstore \ + -Dversion=1.0.0 \ + -DinteractiveMode=false +---- +Remove the following files from the base project since we are not going to use them: + +[source,bash] +---- +cd bookstore +rm src/main/java/org/wildfly/examples/BookStoreService.java +rm src/main/java/org/wildfly/examples/BookStoreEndpoint.java +rm src/test/java/org/wildfly/examples/BookStoreApplicationIT.java +rm src/test/java/org/wildfly/examples/BookStoreServiceIT.java +---- + +=== pom.xml + +==== Jakarta EE dependencies: + +Add the following dependencies to the `pom.xml` file: + +[source,xml] +---- + + jakarta.persistence + jakarta.persistence-api + provided + + + jakarta.transaction + jakarta.transaction-api + provided + + + jakarta.validation + jakarta.validation-api + provided + +---- + +==== Configure WildFly Datasource and trimming server capabilities: +To connect to the database we need to configure the https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem,window=_blank] and install the PostgreSQL driver into the WildFly server. The https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack, window=_blank] contains a set of https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/blob/main/doc/postgresql/README.md[Galleon Layers, window=_blank] that provide *JDBC drivers* and *WildFly Datasource Subsystem* configurations for various databases. For this guide, we will use the `postgresql-default-datasource` Galleon layer that will configure a PostgreSQL datasource as the default datasource for the server. + +In addition to the Galleon Layers to configure the datasource and installing the drivers, we also want to trim the WildFly server to remove any unnecessary subsystems and features we don't need. That will reduce the server footprint and make it more secured. This task can be done by selecting the appropriated https://docs.wildfly.org/32/Galleon_Guide.html#wildfly_galleon_layers[Galleon Layers, window=_blank] shipped with any WildFly distribution. However, instead of adding a static list of Galleon Layers, we are going to configure the `wildfly-maven-plugin` plugin to discover the required layers automatically for us. + +Replace the current `wildfly-maven-plugin` configuration provided by the getting starter guide with the following one in the `pom.xml` file: + +[source,xml] +---- + + org.wildfly.plugins + wildfly-maven-plugin + ${version.wildfly.maven.plugin} + + + + postgresql:default + + + + + + + package + + + + +---- +In the above configuration, behind scenes the `wildfly-maven-plugin` is using https://docs.wildfly.org/wildfly-glow/[WildFly Glow, window=_blank] to discover automatically the required Galleon Layers for our application. The `discover-provisioning-info` configuration tells the plugin to discover the required layers by inspecting our application code. By using `postgresql:default` addon, we are specifying we want to use a PostgreSQL database, and we want to configure it as the default datasource for the server. + +=== persistence.xml +This file is used to configure the Jakarta Persistence Api (JPA) unit and the schema generation strategy. In this guide, we are using the `drop-and-create` strategy to drop the existing schema and create a new one every time the application starts. For a production environment, you should use a more appropriate strategy to avoid data loss. + +Create the following `persistence.xml` file in the `src/main/resources/META-INF` directory: + +[source,xml] +---- + + + + + + + + +---- +NOTE: We don't need to specify the name of the Datasource by using ``. In absence of this property, JPA will use the default datasource configured in the server. + +=== Configure RESTful Web Services (JAX-RS) application + +The `BookStoreApplication` class acts as a configuration class for the JAX-RS application. It essentially tells the JAX-RS runtime that this is a JAX-RS application and provides the base path for the application's RESTful web services. + +Modify it as follows to specify `/api` as the base URL for our JAX-RS Web Service: + +[source,java] +---- +package org.wildfly.examples; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/api") +public class BookStoreApplication extends Application { +} +---- + +=== Book Entity +The `Book` entity represents a book record in the database. + +Create a new class `Book` in the `src/main/java/org/wildfly/examples/books` directory with the following content: + +[source,java] +---- +package org.wildfly.examples.books; + +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.PositiveOrZero; + +@Entity +@Table(name = "books") +public class Book { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @Column(nullable = false) + private String title; + + @NotBlank + @Column(nullable = false) + private String author; + + @NotBlank + @Column(nullable = false) + private String isbn; + + @PositiveOrZero + @Column + private double price; + + public Book() { + } + + public Book(String title, String author, String isbn, double price) { + this.title = title; + this.author = author; + this.isbn = isbn; + this.price = price; + } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getIsbn() { + return isbn; + } + + public void setIsbn(String isbn) { + this.isbn = isbn; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Book book = (Book) o; + return Double.compare(price, book.price) == 0 && Objects.equals(id, book.id) && Objects.equals(title, book.title) && Objects.equals(author, book.author) && Objects.equals(isbn, book.isbn); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, author, isbn, price); + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", title='" + title + '\'' + + ", author='" + author + '\'' + + ", isbn='" + isbn + '\'' + + ", price=" + price + + '}'; + } +} +---- + +=== BookResource +The `BookResource` is the web service that exposes the book records as JSon objects. + +Create a new class `BookResource` in the `src/main/java/org/wildfly/examples/books` directory with the following content: + +[source,java] +---- +package org.wildfly.examples.books; + +import java.net.URI; +import java.util.List; + +import jakarta.enterprise.context.RequestScoped; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; + +@Path("/books") +@RequestScoped +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class BookResource { + + @PersistenceContext + EntityManager em; + + @Context + UriInfo uriInfo; + + @GET + public Response getAll() { + List all = em.createQuery("SELECT b FROM Book b", Book.class) + .getResultList(); + + return Response.ok() + .entity(all) + .build(); + } + + @GET + @Path("/{id}") + public Response getById(@PathParam("id") Long id) { + Book book = em.find(Book.class, id); + if (book == null) { + throw new NotFoundException("Book with id " + id + " not found"); + } + + return Response.ok() + .entity(book) + .build(); + } + + @POST + @Transactional + public Response create(@Valid Book book) { + em.persist(book); + + final URI location = uriInfo.getBaseUriBuilder() + .path(BookResource.class) + .path(book.getId().toString()) + .build(); + + return Response.created(location) + .entity(book) + .build(); + } + + @PUT + @Path("/{id}") + @Transactional + public Response update(@PathParam("id") Long id, @Valid Book book) { + Book existing = em.find(Book.class, id); + if (existing == null) { + throw new NotFoundException("Book with id " + id + " not found"); + } + existing.setAuthor(book.getAuthor()); + existing.setTitle(book.getTitle()); + existing.setIsbn(book.getIsbn()); + existing.setPrice(book.getPrice()); + + return Response.ok() + .entity(existing) + .build(); + } + + @DELETE + @Path("/{id}") + @Transactional + public Response delete(@PathParam("id") Long id) { + Book book = em.find(Book.class, id); + if (book == null) { + throw new NotFoundException("Book with id " + id + " not found"); + } + em.remove(book); + + return Response.noContent() + .build(); + } +} +---- + +== Start the application + +Now we should be ready to start our application and interact with the database. First, build the application using Maven: + +[source,bash,subs="normal"] +---- +mvn clean package +---- + +Notice how WildFly Glow gives us information about the Feature packs and Galleon layers discovered. It also provides some hints about required environment variables: + +[source,bash] +---- +[INFO] --- wildfly:5.0.0.Final:package (default) @ bookstore --- +[INFO] Glow is scanning... +[INFO] Glow scanning DONE. +[INFO] context: bare-metal +[INFO] enabled profile: none +[INFO] galleon discovery +[INFO] - feature-packs + org.wildfly:wildfly-galleon-pack:32.0.1.Final + org.wildfly:wildfly-datasources-galleon-pack:8.0.0.Final +- layers + ee-core-profile-server + jaxrs + jpa + postgresql-default-datasource + +[INFO] enabled add-ons +[INFO] - postgresql : Documentation in https://github.com/wildfly-extras/wildfly-datasources-galleon-pack +- postgresql:default : Documentation in https://github.com/wildfly-extras/wildfly-datasources-galleon-pack + +[INFO] identified fixes +[INFO] * no default datasource found error is fixed + - add-on postgresql:default fixes the problem but you need to set the strongly suggested configuration. + +[WARNING] strongly suggested configuration at runtime +[WARNING] +postgresql-datasource environment variables: + - POSTGRESQL_DATABASE=Defines the database name to be used in the datasource’s `connection-url` property. + - POSTGRESQL_PASSWORD=Defines the password for the datasource. + - POSTGRESQL_USER=Defines the username for the datasource. +[WARNING] +postgresql-default-datasource environment variables: + - POSTGRESQL_DATABASE=Defines the database name to be used in the datasource’s `connection-url` property. + - POSTGRESQL_PASSWORD=Defines the password for the datasource. + - POSTGRESQL_USER=Defines the username for the datasource. +---- + +Now create the required *environment variables* used by WildFly to connect to the PostgreSQL database and start the server: + +[source,bash,subs="normal"] +---- +export POSTGRESQL_USER={postgre-sql-user} +export POSTGRESQL_PASSWORD={postgre-sql-password} +export POSTGRESQL_DATABASE={postgre-sql-database} + +./target/server/bin/standalone.sh +... +11:34:49,242 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 32.0.1.Final (WildFly Core 24.0.1.Final) started in 2118ms - Started 295 of 366 services (139 services are lazy, passive or on-demand) - Server configuration file in use: standalone.xml +---- + +== Check the application + +We have our application running at http://localhost:8080/. Let's now interact with it using the following endpoints to Create, Read, Update and Delete books. We will use the `curl` utility to interact with the application. + +=== Create a book +To create a new book, execute a POST request to the `/api/books` endpoint with the book information: + +[source,bash] +---- +$ curl -v -X POST http://localhost:8080/api/books -H "Content-Type: application/json" -d ' +{ +"author": "Jules Verne", +"isbn": "10-0760765197", +"price": 9.99, +"title": "From the Earth to the Moon" +}' +---- + +If you inspect the response, you will see the URL of the newly created book gets returned under the location header: + +[source,bash] +---- +Location: http://localhost:8080/api/books/1 +---- + +You can use the location to check the book you have just created: + +[source,bash] +---- +$ curl http://localhost:8080/api/books/1 +---- +[source,json] +---- +{ + "author": "Jules Verne", + "id": 1, + "isbn": "10-0760765197", + "price": 9.99, + "title": "From the Earth to the Moon" +} +---- + +=== Read all the books +To list all the books, execute a GET request to the `/api/books` endpoint: + +[source,bash] +---- +$ curl http://localhost:8080/api/books +---- + +It will return the list of books of our database: + +[source,json] +---- +[ + { + "author": "Jules Verne", + "id": 1, + "isbn": "10-0760765197", + "price": 9.99, + "title": "From the Earth to the Moon" + } +] +---- + +=== Update a book +To update a book, execute a PUT request to the `/api/books/{id}` endpoint with the book information you want. For example to change the price of the recent book we have recently created, execute the following: + +[source,bash] +---- +$ curl -X PUT http://localhost:8080/api/books/1 -H "Content-Type: application/json" -d ' +{ +"author": "Jules Verne", +"isbn": "10-0760765197", +"price": 10.99, +"title": "From the Earth to the Moon" +}' +---- + +=== Delete a book +To delete a book, execute a DELETE request to the `/api/books/{id}` endpoint with the book id you want to delete. For example, to delete the book we have recently created: + +[source,bash] +---- +$ curl -X DELETE http://localhost:8080/api/books/1 +---- + +==== Stop the application + +To stop the application, press `Ctrl+C` in the terminal where the server is running. + + +== Test Cases + +Until now, we have verified the application manually. The following steps will guide you with the required changes to test our application using the Arquillian framework. + +=== pom.xml +We need to have a JSON provider available on the test classpath to convert Book objects to JSON and vice versa. + +Add the following dependency to the `pom.xml` file: + +[source,xml] +---- + + org.jboss.resteasy + resteasy-jackson2-provider + test + +---- + +=== Book Resource test case + +Create a new class `BookResourceIT` in the `src/test/java/org/wildfly/examples/books` directory with the following content: + +[source,java] +---- +package org.wildfly.examples.books; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URI; +import java.util.List; + +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.GenericType; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.jboss.arquillian.container.test.api.RunAsClient; +import org.jboss.arquillian.junit5.ArquillianExtension; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; + +@RunAsClient +@ExtendWith(ArquillianExtension.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BookResourceIT { + + @Test + @Order(1) + public void testHelloEndpoint() { + try (Client client = ClientBuilder.newClient()) { + Response response = client + .target(URI.create("http://localhost:8080/")) + .path("/") + .request() + .get(); + + assertEquals(200, response.getStatus()); + } + } + + @Test + @Order(2) + public void create() { + try (Client client = ClientBuilder.newClient()) { + Book book = new Book("Test Book title", "Test Book author", "Test Book isbn", 10.0); + + Response response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books") + .request(MediaType.APPLICATION_JSON) + .post(Entity.entity(book, MediaType.APPLICATION_JSON)); + + assertEquals(201, response.getStatus()); + assertEquals("http://localhost:8080/api/books/1", response.getLocation().toString()); + } + } + + @Test + @Order(3) + public void list() { + try (Client client = ClientBuilder.newClient()) { + Response response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books") + .request(MediaType.APPLICATION_JSON) + .get(); + + assertEquals(200, response.getStatus()); + + List books = response.readEntity(new GenericType<>() { + }); + assertEquals(1, books.size()); + + Book book = books.get(0); + assertEquals("Test Book title", book.getTitle()); + assertEquals("Test Book author", book.getAuthor()); + assertEquals("Test Book isbn", book.getIsbn()); + assertEquals(10.0, book.getPrice()); + } + } + + @Test + @Order(4) + public void update() { + try (Client client = ClientBuilder.newClient()) { + Book book = new Book("Test Book title updated", "Test Book author updated", "Test Book isbn updated", 99.9); + + Response response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books/1") + .request(MediaType.APPLICATION_JSON) + .put(Entity.entity(book, MediaType.APPLICATION_JSON)); + + assertEquals(200, response.getStatus()); + + response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books/1") + .request(MediaType.APPLICATION_JSON) + .get(); + + Book updated = response.readEntity(new GenericType<>() { + }); + + assertEquals("Test Book title updated", updated.getTitle()); + assertEquals("Test Book author updated", updated.getAuthor()); + assertEquals("Test Book isbn updated", updated.getIsbn()); + assertEquals(99.9, updated.getPrice()); + } + } + + @Test + @Order(5) + public void delete() { + try (Client client = ClientBuilder.newClient()) { + Response response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books/1") + .request(MediaType.APPLICATION_JSON) + .delete(); + + assertEquals(204, response.getStatus()); + + response = client + .target(URI.create("http://localhost:8080/")) + .path("/api/books/1") + .request(MediaType.APPLICATION_JSON) + .get(); + + assertEquals(404, response.getStatus()); + } + } +} +---- + +=== Run the tests +You can run the tests using the following command: + +[source,bash,subs="normal"] +---- +export POSTGRESQL_USER={postgre-sql-user} +export POSTGRESQL_PASSWORD={postgre-sql-password} +export POSTGRESQL_DATABASE={postgre-sql-database} + +mvn clean verify +---- + +In this guide we have reused the same database instance for running the application and for the test cases. If you want to use a different instance for test cases, you have to adapt the values of the environment variables accordingly. + +=== Stop the database + +Finally, to stop the PostgreSQL database, press `Ctrl+C` in the terminal where the container is running. + + +// Always keep a what's next? section to let the user know what could be achieved next +== What's next? + +In this guide we have learnt how to configure a WildFly server to access to a PostgreSQL database and how to easily trim the server capabilities using WildFly Glow. Seamlessly, you can adapt the same application to use other databases by changing the Galleon Layers used by the WildFly server. +You can learn more about how to configure WildFly for other databases by looking at the https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack] documentation and https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide]. + +// Always add this section last to link to any relevant content +[[references]] +== References + +* https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem] +* https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack] +* https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide]. From 8993f981ea309f325f5270b04b29964b88b0bed5 Mon Sep 17 00:00:00 2001 From: Yeray Borges Date: Mon, 26 Aug 2024 12:22:27 +0100 Subject: [PATCH 2/4] [#577] Guide: Integrating with a PostgreSQL database --- _data/guides.yaml | 2 +- .../database-integrating-with-postgresql.adoc | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/_data/guides.yaml b/_data/guides.yaml index 6578d844..f9da7d9b 100644 --- a/_data/guides.yaml +++ b/_data/guides.yaml @@ -60,5 +60,5 @@ categories: guides: - title: Integrating with a PostgreSQL database url: /guides/database-integrating-with-postgresql - description: Learn how to configure a datasource to connect a PostgreSQL database. + description: Learn how to configure a datasource to connect to a PostgreSQL database. diff --git a/guides/database-integrating-with-postgresql.adoc b/guides/database-integrating-with-postgresql.adoc index 723105e5..f16efc17 100644 --- a/guides/database-integrating-with-postgresql.adoc +++ b/guides/database-integrating-with-postgresql.adoc @@ -11,7 +11,7 @@ include::{includedir}/_attributes.adoc[] :postgre-docker-image: docker.io/library/postgres :postgre-sql-kubernetes-service-name: postgres-service -In this guide, you will learn how to configure WildFly to connect to a PostgreSQL database. You will create a simple Book Store API application to manage books stored in the database using a https://jakarta.ee/specifications/restful-ws/[Jakarta RESTful Web Services (JAX-RS), window=_blank]. +In this guide, you will learn how to configure WildFly to connect to a PostgreSQL database. You will create a simple Book Store API application to manage books stored in the database using https://jakarta.ee/specifications/restful-ws/[Jakarta RESTful Web Services (JAX-RS), window=_blank]. include::{includedir}/_prerequisites.adoc[] @@ -21,7 +21,7 @@ include::{includedir}/_prerequisites.adoc[] === PostgreSQL -We will use PostgreSQL as database server in its containerized version: see https://hub.docker.com/_/postgres[PostgreSQL Official Image, window=_blank]. +We will use PostgreSQL as the database server in its containerized version: see https://hub.docker.com/_/postgres[PostgreSQL Official Image, window=_blank]. Start PostgreSQL database in a container with: @@ -35,14 +35,14 @@ podman run --rm --name bookstore \ {postgre-docker-image} ---- -NOTE: we started the container with the `--rm` so it can be disposed automatically when we stop it. +NOTE: we started the container with the `--rm` flag so it can be disposed of automatically when we stop it. == Application === Create a new Maven project -We are going to use the *WildFly Getting Started Archetype* to create the base structure of our Book Store API. +We are going to use the *WildFly Getting Started Archetype* to create the base structure of our Book Store API application. Open a new terminal window and create a new project using the WildFly Getting Started Archetype: @@ -95,9 +95,9 @@ Add the following dependencies to the `pom.xml` file: ==== Configure WildFly Datasource and trimming server capabilities: To connect to the database we need to configure the https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem,window=_blank] and install the PostgreSQL driver into the WildFly server. The https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack, window=_blank] contains a set of https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/blob/main/doc/postgresql/README.md[Galleon Layers, window=_blank] that provide *JDBC drivers* and *WildFly Datasource Subsystem* configurations for various databases. For this guide, we will use the `postgresql-default-datasource` Galleon layer that will configure a PostgreSQL datasource as the default datasource for the server. -In addition to the Galleon Layers to configure the datasource and installing the drivers, we also want to trim the WildFly server to remove any unnecessary subsystems and features we don't need. That will reduce the server footprint and make it more secured. This task can be done by selecting the appropriated https://docs.wildfly.org/32/Galleon_Guide.html#wildfly_galleon_layers[Galleon Layers, window=_blank] shipped with any WildFly distribution. However, instead of adding a static list of Galleon Layers, we are going to configure the `wildfly-maven-plugin` plugin to discover the required layers automatically for us. +In addition to the Galleon Layers to configure the datasource and install the drivers, we also want to trim the WildFly server to remove any unnecessary subsystems and features we don't need. That will reduce the server footprint and the security attack surface. This task can be done by selecting the appropriate https://docs.wildfly.org/32/Galleon_Guide.html#wildfly_galleon_layers[Galleon Layers, window=_blank] shipped with any WildFly distribution. However, instead of adding a static list of Galleon Layers, we are going to configure the `wildfly-maven-plugin` plugin to discover the required layers automatically for us. -Replace the current `wildfly-maven-plugin` configuration provided by the getting starter guide with the following one in the `pom.xml` file: +Replace the current `wildfly-maven-plugin` configuration in the `pom.xml` file provided by the getting started guide with the following one: [source,xml] ---- @@ -121,10 +121,10 @@ Replace the current `wildfly-maven-plugin` configuration provided by the getting ---- -In the above configuration, behind scenes the `wildfly-maven-plugin` is using https://docs.wildfly.org/wildfly-glow/[WildFly Glow, window=_blank] to discover automatically the required Galleon Layers for our application. The `discover-provisioning-info` configuration tells the plugin to discover the required layers by inspecting our application code. By using `postgresql:default` addon, we are specifying we want to use a PostgreSQL database, and we want to configure it as the default datasource for the server. +In the above configuration, behind the scenes the `wildfly-maven-plugin` is using https://docs.wildfly.org/wildfly-glow/[WildFly Glow, window=_blank] to discover automatically the required Galleon Layers for our application. The `discover-provisioning-info` configuration tells the plugin to discover the required layers by inspecting our application code. By using the `postgresql:default` addon, we are specifying we want to use a PostgreSQL database, and we want to configure it as the default datasource for the server. === persistence.xml -This file is used to configure the Jakarta Persistence Api (JPA) unit and the schema generation strategy. In this guide, we are using the `drop-and-create` strategy to drop the existing schema and create a new one every time the application starts. For a production environment, you should use a more appropriate strategy to avoid data loss. +This file is used to configure the Jakarta Persistence API (JPA) persistence unit and its database schema generation strategy. In this guide, we are using the `drop-and-create` strategy to drop the existing schema and create a new one every time the application starts. For a production environment, you should use a more appropriate strategy to avoid data loss. Create the following `persistence.xml` file in the `src/main/resources/META-INF` directory: @@ -143,7 +143,7 @@ Create the following `persistence.xml` file in the `src/main/resources/META-INF` ---- NOTE: We don't need to specify the name of the Datasource by using ``. In absence of this property, JPA will use the default datasource configured in the server. -=== Configure RESTful Web Services (JAX-RS) application +=== Configure the Jakarta RESTful Web Services (JAX-RS) application The `BookStoreApplication` class acts as a configuration class for the JAX-RS application. It essentially tells the JAX-RS runtime that this is a JAX-RS application and provides the base path for the application's RESTful web services. @@ -277,7 +277,7 @@ public class Book { ---- === BookResource -The `BookResource` is the web service that exposes the book records as JSon objects. +The `BookResource` is the web service that exposes the book records as JSON objects. Create a new class `BookResource` in the `src/main/java/org/wildfly/examples/books` directory with the following content: @@ -400,7 +400,7 @@ Now we should be ready to start our application and interact with the database. mvn clean package ---- -Notice how WildFly Glow gives us information about the Feature packs and Galleon layers discovered. It also provides some hints about required environment variables: +Notice how WildFly Glow gives us information about the feature packs and Galleon layers discovered. It also provides some hints about required environment variables: [source,bash] ---- @@ -727,7 +727,7 @@ Finally, to stop the PostgreSQL database, press `Ctrl+C` in the terminal where t // Always keep a what's next? section to let the user know what could be achieved next == What's next? -In this guide we have learnt how to configure a WildFly server to access to a PostgreSQL database and how to easily trim the server capabilities using WildFly Glow. Seamlessly, you can adapt the same application to use other databases by changing the Galleon Layers used by the WildFly server. +In this guide we have learned how to configure a WildFly server to access to a PostgreSQL database and how to easily trim the server capabilities using WildFly Glow. Seamlessly, you can adapt the same application to use other databases by changing the Galleon Layers used by the WildFly server. You can learn more about how to configure WildFly for other databases by looking at the https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack] documentation and https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide]. // Always add this section last to link to any relevant content From 1998b55af8af75b25a1d1a93b245f19a91a4c2e2 Mon Sep 17 00:00:00 2001 From: Yeray Borges Date: Mon, 26 Aug 2024 16:46:01 +0100 Subject: [PATCH 3/4] [#577] Review Jakarta REST / JAX-RS usage --- guides/database-integrating-with-postgresql.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/guides/database-integrating-with-postgresql.adoc b/guides/database-integrating-with-postgresql.adoc index f16efc17..10617920 100644 --- a/guides/database-integrating-with-postgresql.adoc +++ b/guides/database-integrating-with-postgresql.adoc @@ -11,7 +11,7 @@ include::{includedir}/_attributes.adoc[] :postgre-docker-image: docker.io/library/postgres :postgre-sql-kubernetes-service-name: postgres-service -In this guide, you will learn how to configure WildFly to connect to a PostgreSQL database. You will create a simple Book Store API application to manage books stored in the database using https://jakarta.ee/specifications/restful-ws/[Jakarta RESTful Web Services (JAX-RS), window=_blank]. +In this guide, you will learn how to configure WildFly to connect to a PostgreSQL database. You will create a simple Book Store API application to manage books stored in the database using https://jakarta.ee/specifications/restful-ws/[Jakarta RESTful Web Services (Jakarta REST), window=_blank]. include::{includedir}/_prerequisites.adoc[] @@ -143,11 +143,11 @@ Create the following `persistence.xml` file in the `src/main/resources/META-INF` ---- NOTE: We don't need to specify the name of the Datasource by using ``. In absence of this property, JPA will use the default datasource configured in the server. -=== Configure the Jakarta RESTful Web Services (JAX-RS) application +=== Configure the Jakarta RESTful Web Services application -The `BookStoreApplication` class acts as a configuration class for the JAX-RS application. It essentially tells the JAX-RS runtime that this is a JAX-RS application and provides the base path for the application's RESTful web services. +The `BookStoreApplication` class acts as a configuration class for the Jakarta REST application. It essentially tells the WildFly runtime that this is a Jakarta REST application and provides the base path for the application's RESTful web services. -Modify it as follows to specify `/api` as the base URL for our JAX-RS Web Service: +Modify it as follows to specify `/api` as the base URL for our Jakarta REST Web Service: [source,java] ---- From f09e2f118a347a75a6909c1d76986eca9b391090 Mon Sep 17 00:00:00 2001 From: Yeray Borges Date: Tue, 27 Aug 2024 12:19:00 +0100 Subject: [PATCH 4/4] [#577] Review JPA usages, added link for wildfly archetype github project --- guides/database-integrating-with-postgresql.adoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/guides/database-integrating-with-postgresql.adoc b/guides/database-integrating-with-postgresql.adoc index 10617920..381cc233 100644 --- a/guides/database-integrating-with-postgresql.adoc +++ b/guides/database-integrating-with-postgresql.adoc @@ -42,7 +42,7 @@ NOTE: we started the container with the `--rm` flag so it can be disposed of aut === Create a new Maven project -We are going to use the *WildFly Getting Started Archetype* to create the base structure of our Book Store API application. +We are going to use the https://github.com/wildfly/wildfly-archetypes/tree/main/wildfly-getting-started-archetype[WildFly Getting Started Archetype, window=_blank] to create the base structure of our Book Store API application. Open a new terminal window and create a new project using the WildFly Getting Started Archetype: @@ -93,7 +93,7 @@ Add the following dependencies to the `pom.xml` file: ---- ==== Configure WildFly Datasource and trimming server capabilities: -To connect to the database we need to configure the https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem,window=_blank] and install the PostgreSQL driver into the WildFly server. The https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack, window=_blank] contains a set of https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/blob/main/doc/postgresql/README.md[Galleon Layers, window=_blank] that provide *JDBC drivers* and *WildFly Datasource Subsystem* configurations for various databases. For this guide, we will use the `postgresql-default-datasource` Galleon layer that will configure a PostgreSQL datasource as the default datasource for the server. +To connect to the database we need to configure the https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem,window=_blank] and install the PostgreSQL driver into the WildFly server. The https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasources Galleon Pack, window=_blank] contains a set of https://github.com/wildfly-extras/wildfly-datasources-galleon-pack/blob/main/doc/postgresql/README.md[Galleon Layers, window=_blank] that provide *JDBC drivers* and *WildFly Datasource Subsystem* configurations for various databases. For this guide, we will use the `postgresql-default-datasource` Galleon layer that will configure a PostgreSQL datasource as the default datasource for the server. In addition to the Galleon Layers to configure the datasource and install the drivers, we also want to trim the WildFly server to remove any unnecessary subsystems and features we don't need. That will reduce the server footprint and the security attack surface. This task can be done by selecting the appropriate https://docs.wildfly.org/32/Galleon_Guide.html#wildfly_galleon_layers[Galleon Layers, window=_blank] shipped with any WildFly distribution. However, instead of adding a static list of Galleon Layers, we are going to configure the `wildfly-maven-plugin` plugin to discover the required layers automatically for us. @@ -124,7 +124,7 @@ Replace the current `wildfly-maven-plugin` configuration in the `pom.xml` file p In the above configuration, behind the scenes the `wildfly-maven-plugin` is using https://docs.wildfly.org/wildfly-glow/[WildFly Glow, window=_blank] to discover automatically the required Galleon Layers for our application. The `discover-provisioning-info` configuration tells the plugin to discover the required layers by inspecting our application code. By using the `postgresql:default` addon, we are specifying we want to use a PostgreSQL database, and we want to configure it as the default datasource for the server. === persistence.xml -This file is used to configure the Jakarta Persistence API (JPA) persistence unit and its database schema generation strategy. In this guide, we are using the `drop-and-create` strategy to drop the existing schema and create a new one every time the application starts. For a production environment, you should use a more appropriate strategy to avoid data loss. +This file is used to configure the Jakarta Persistence persistence unit and its database schema generation strategy. In this guide, we are using the `drop-and-create` strategy to drop the existing schema and create a new one every time the application starts. For a production environment, you should use a more appropriate strategy to avoid data loss. Create the following `persistence.xml` file in the `src/main/resources/META-INF` directory: @@ -141,7 +141,7 @@ Create the following `persistence.xml` file in the `src/main/resources/META-INF` ---- -NOTE: We don't need to specify the name of the Datasource by using ``. In absence of this property, JPA will use the default datasource configured in the server. +NOTE: We don't need to specify the name of the Datasource by using ``. In absence of this property, Jakarta Persistence will use the default datasource configured in the server. === Configure the Jakarta RESTful Web Services application @@ -728,12 +728,12 @@ Finally, to stop the PostgreSQL database, press `Ctrl+C` in the terminal where t == What's next? In this guide we have learned how to configure a WildFly server to access to a PostgreSQL database and how to easily trim the server capabilities using WildFly Glow. Seamlessly, you can adapt the same application to use other databases by changing the Galleon Layers used by the WildFly server. -You can learn more about how to configure WildFly for other databases by looking at the https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack] documentation and https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide]. +You can learn more about how to configure WildFly for other databases by looking at the https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasources Galleon Pack] documentation and https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide]. // Always add this section last to link to any relevant content [[references]] == References * https://docs.wildfly.org/32/Admin_Guide.html#DataSource[WildFly Datasource Subsystem] -* https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasource Galleon Pack] +* https://github.com/wildfly-extras/wildfly-datasources-galleon-pack[WildFly Datasources Galleon Pack] * https://docs.wildfly.org/32/Glow_Guide.html[WildFly Glow Guide].