diff --git a/auditlog/.gitignore b/auditlog/.gitignore new file mode 100644 index 0000000000..7701859592 --- /dev/null +++ b/auditlog/.gitignore @@ -0,0 +1,3 @@ +target +.quarkus +connector-config \ No newline at end of file diff --git a/auditlog/README.md b/auditlog/README.md index e83f4b7560..3ddecfe55a 100755 --- a/auditlog/README.md +++ b/auditlog/README.md @@ -5,37 +5,37 @@ It accompanies the blog post [Building Audit Logs with Change Data Capture and S There are two applications (based on [Quarkus](https://quarkus.io/)): -* _vegetables-service_: a simple REST service for inserting and updating vegetable data into a Postgres database; -as part of its processing, it will not only update its actual "business table" `vegetable`, -but also insert some auditing metadata into a dedicated metadata table `transaction_context_data`: -the user (as obtained from the passed JWT token), the client's date (as passed via the HTTP 1.1 `Date` header) -and a use case identifier (as specified in an annotation on the REST API methods). -* _log-enricher_: a Kafka Streams application, -which joins the CDC topic holding the `vegetable` change events (`dbserver1.inventory.vegetable`) with the corresponding metadata in the `dbserver1.inventory.transaction_context_data` topic sourced from the `transaction_context_data` table; -this table is keyed by transaction id, allowing for joining the vegetable `KStream` with the metadata `KTable`. -The enriched vegetable change events are written to the `dbserver1.inventory.vegetable.enriched` topic. +- _vegetables-service_: a simple REST service for inserting and updating vegetable data into a Postgres database; + as part of its processing, it will not only update its actual "business table" `vegetable`, + but also insert some auditing metadata into a dedicated metadata table `transaction_context_data`: + the user (as obtained from the passed JWT token), the client's date (as passed via the HTTP 1.1 `Date` header) + and a use case identifier (as specified in an annotation on the REST API methods). +- _log-enricher_: a Kafka Streams application, + which joins the CDC topic holding the `vegetable` change events (`dbserver1.inventory.vegetable`) with the corresponding metadata in the `dbserver1.inventory.transaction_context_data` topic sourced from the `transaction_context_data` table; + this table is keyed by transaction id, allowing for joining the vegetable `KStream` with the metadata `KTable`. + The enriched vegetable change events are written to the `dbserver1.inventory.vegetable.enriched` topic. ## Building the Demo ```console -$ mvn clean package +mvn clean package ``` ```console -$ export DEBEZIUM_VERSION=1.8 -$ docker-compose up --build +export DEBEZIUM_VERSION=3.4 +docker-compose up --build ``` ## Deploy the Debezium Postgres Connector ```console -$ http PUT http://localhost:8083/connectors/inventory-connector/config < register-postgres.json +http PUT http://localhost:8083/connectors/inventory-connector/config < register-postgres.json ``` ## Modifying Some Data and Observing the Audit Log ```console -$ http POST http://localhost:8080/vegetables 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJib2IiLCJ1cG4iOiJmYXJtZXJib2IiLCJhdXRoX3RpbWUiOjE1NjY0NTgxMTMsImlzcyI6ImZhcm1zaG9wIiwiZ3JvdXBzIjpbImZhcm1lcnMiLCJjdXN0b21lcnMiXSwiZXhwIjo0MTAyNDQ0Nzk5LCJpYXQiOjE1NjY0NTgxMTMsImp0aSI6IjQyIn0.CscbJN8amqKryYvnVO1184J8F67HN2iTEjVN2VOPodcnoeOd7_iQVKUjC3h-ye5apkJjvAsQKrjzlrGCHRfl-n6jC9F7IkOtjoWnJ4wQ9BBo1SAtPw_Czt1I_Ujm-Kb1p5-BWACCBCVVFgYZTWP_laz5JZS7dIvs6VqoNnw7A4VpA6iPfTVfYlNY3u86-k1FvEg_hW-N9Y9RuihMsPuTdpHK5xdjCrJiD0VJ7-0eRQ8RXpycHuHN4xfmV8MqXBYjYSYDOhbnYbdQVbf0YJoFFqfb75my5olN-97ITsi2MS62W_y-RNT0qZrbytqINA3fF3VQsSY6VcaqRAeygrKm_Q' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' name=Tomatoe description=Yummy! +http POST http://localhost:8080/vegetables 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJib2IiLCJ1cG4iOiJmYXJtZXJib2IiLCJhdXRoX3RpbWUiOjE1NjY0NTgxMTMsImlzcyI6ImZhcm1zaG9wIiwiZ3JvdXBzIjpbImZhcm1lcnMiLCJjdXN0b21lcnMiXSwiZXhwIjo0MTAyNDQ0Nzk5LCJpYXQiOjE1NjY0NTgxMTMsImp0aSI6IjQyIn0.CscbJN8amqKryYvnVO1184J8F67HN2iTEjVN2VOPodcnoeOd7_iQVKUjC3h-ye5apkJjvAsQKrjzlrGCHRfl-n6jC9F7IkOtjoWnJ4wQ9BBo1SAtPw_Czt1I_Ujm-Kb1p5-BWACCBCVVFgYZTWP_laz5JZS7dIvs6VqoNnw7A4VpA6iPfTVfYlNY3u86-k1FvEg_hW-N9Y9RuihMsPuTdpHK5xdjCrJiD0VJ7-0eRQ8RXpycHuHN4xfmV8MqXBYjYSYDOhbnYbdQVbf0YJoFFqfb75my5olN-97ITsi2MS62W_y-RNT0qZrbytqINA3fF3VQsSY6VcaqRAeygrKm_Q' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' name=Tomatoe description=Yummy! ``` This uses a pre-generated JWT token (with expiration date set to 2099-12-31 and user set to "farmerbob"). @@ -44,13 +44,13 @@ To regenerate the token with different data, use the [Jwtenizr](https://github.c You can also update an existing vegetable record (this token is for "farmermargaret"): ```console -$ http PUT http://localhost:8080/vegetables/10 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJtYXJnYXJldCIsInVwbiI6ImZhcm1lcm1hcmdhcmV0IiwiYXV0aF90aW1lIjoxNTY5ODM1Mzk5LCJpc3MiOiJmYXJtc2hvcCIsImdyb3VwcyI6WyJmYXJtZXJzIiwiY3VzdG9tZXJzIl0sImV4cCI6NDEwMjQ0NDc5OSwiaWF0IjoxNTY5ODM1Mzk5LCJqdGkiOiI0MiJ9.DTEUA3p-xyK5nveoJIVhjfKNFdVszYIb55Qj4Xrm70DDbAXuOU2FMkffuUAUm2s7ACkp2KEmg6brRwSjvA-zhW61kDR9ZgEb9NWeDjr6Eue08xcSODKt7SGV-M7h3yhuDIhU7uaZrxRUAQTWqm1vxd2rmN_QH0frhKMUNFFsLIOGLG0zHcLosRcwZ4tAKXSSB9VE0fth6srIQCUebDkF7ucA_WSYjPRvahCBd8JvnV4VUGQxZW8zcRhTEwcaLq20ODO-dr85xgWI2Yr_1A7PDuDL4oUjCb90YyhtzaIzs2vQMjcxJ6TWmTcqJpgCfkjE-TeVwjaafcNJu0fBmcP8jA' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' name=Tomatoe description=Tasty! +http PUT http://localhost:8080/vegetables/10 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJtYXJnYXJldCIsInVwbiI6ImZhcm1lcm1hcmdhcmV0IiwiYXV0aF90aW1lIjoxNTY5ODM1Mzk5LCJpc3MiOiJmYXJtc2hvcCIsImdyb3VwcyI6WyJmYXJtZXJzIiwiY3VzdG9tZXJzIl0sImV4cCI6NDEwMjQ0NDc5OSwiaWF0IjoxNTY5ODM1Mzk5LCJqdGkiOiI0MiJ9.DTEUA3p-xyK5nveoJIVhjfKNFdVszYIb55Qj4Xrm70DDbAXuOU2FMkffuUAUm2s7ACkp2KEmg6brRwSjvA-zhW61kDR9ZgEb9NWeDjr6Eue08xcSODKt7SGV-M7h3yhuDIhU7uaZrxRUAQTWqm1vxd2rmN_QH0frhKMUNFFsLIOGLG0zHcLosRcwZ4tAKXSSB9VE0fth6srIQCUebDkF7ucA_WSYjPRvahCBd8JvnV4VUGQxZW8zcRhTEwcaLq20ODO-dr85xgWI2Yr_1A7PDuDL4oUjCb90YyhtzaIzs2vQMjcxJ6TWmTcqJpgCfkjE-TeVwjaafcNJu0fBmcP8jA' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' name=Tomatoe description=Tasty! ``` Or delete a record (again using the "farmerbob" token): ```console -$ http DELETE http://localhost:8080/vegetables/10 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJib2IiLCJ1cG4iOiJmYXJtZXJib2IiLCJhdXRoX3RpbWUiOjE1NjY0NTgxMTMsImlzcyI6ImZhcm1zaG9wIiwiZ3JvdXBzIjpbImZhcm1lcnMiLCJjdXN0b21lcnMiXSwiZXhwIjo0MTAyNDQ0Nzk5LCJpYXQiOjE1NjY0NTgxMTMsImp0aSI6IjQyIn0.CscbJN8amqKryYvnVO1184J8F67HN2iTEjVN2VOPodcnoeOd7_iQVKUjC3h-ye5apkJjvAsQKrjzlrGCHRfl-n6jC9F7IkOtjoWnJ4wQ9BBo1SAtPw_Czt1I_Ujm-Kb1p5-BWACCBCVVFgYZTWP_laz5JZS7dIvs6VqoNnw7A4VpA6iPfTVfYlNY3u86-k1FvEg_hW-N9Y9RuihMsPuTdpHK5xdjCrJiD0VJ7-0eRQ8RXpycHuHN4xfmV8MqXBYjYSYDOhbnYbdQVbf0YJoFFqfb75my5olN-97ITsi2MS62W_y-RNT0qZrbytqINA3fF3VQsSY6VcaqRAeygrKm_Q' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' +http DELETE http://localhost:8080/vegetables/10 'Authorization:Bearer eyJraWQiOiJqd3Qua2V5IiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmYXJtZXJib2IiLCJ1cG4iOiJmYXJtZXJib2IiLCJhdXRoX3RpbWUiOjE1NjY0NTgxMTMsImlzcyI6ImZhcm1zaG9wIiwiZ3JvdXBzIjpbImZhcm1lcnMiLCJjdXN0b21lcnMiXSwiZXhwIjo0MTAyNDQ0Nzk5LCJpYXQiOjE1NjY0NTgxMTMsImp0aSI6IjQyIn0.CscbJN8amqKryYvnVO1184J8F67HN2iTEjVN2VOPodcnoeOd7_iQVKUjC3h-ye5apkJjvAsQKrjzlrGCHRfl-n6jC9F7IkOtjoWnJ4wQ9BBo1SAtPw_Czt1I_Ujm-Kb1p5-BWACCBCVVFgYZTWP_laz5JZS7dIvs6VqoNnw7A4VpA6iPfTVfYlNY3u86-k1FvEg_hW-N9Y9RuihMsPuTdpHK5xdjCrJiD0VJ7-0eRQ8RXpycHuHN4xfmV8MqXBYjYSYDOhbnYbdQVbf0YJoFFqfb75my5olN-97ITsi2MS62W_y-RNT0qZrbytqINA3fF3VQsSY6VcaqRAeygrKm_Q' 'Date:Thu, 22 Aug 2019 08:12:31 GMT' ``` Doing so, observe the contents of the `dbserver1.inventory.vegetable`, `dbserver1.inventory.transaction_context_data` and `dbserver1.inventory.vegetable.enriched` topics: @@ -58,19 +58,19 @@ Doing so, observe the contents of the `dbserver1.inventory.vegetable`, `dbserver ```console $ docker run -it --rm \ --network auditlog_default \ - quay.io/debezium/tooling:1.2 \ + quay.io/debezium/tooling:2.0 \ /bin/bash -c "kafkacat -b kafka:9092 \ -C -o beginning -q -u -t dbserver1.inventory.vegetable | jq ." $ docker run -it --rm \ --network auditlog_default \ - quay.io/debezium/tooling:1.2 \ + quay.io/debezium/tooling:2.0 \ /bin/bash -c "kafkacat -b kafka:9092 \ -C -o beginning -q -u -t dbserver1.inventory.transaction_context_data | jq ." $ docker run -it --rm \ --network auditlog_default \ - quay.io/debezium/tooling:1.2 \ + quay.io/debezium/tooling:2.0 \ /bin/bash -c "kafkacat -b kafka:9092 \ -C -o beginning -q -u -t dbserver1.inventory.vegetable.enriched | jq ." ``` @@ -105,7 +105,7 @@ create a task for administrator to provide the missing data. ```console $ docker run --tty --rm -i \ --network auditlog_default \ - quay.io/debezium/tooling:1.2 \ + quay.io/debezium/tooling:2.0 \ bash -c 'pgcli postgresql://postgresuser:postgrespw@vegetables-db:5432/vegetablesdb' ``` @@ -147,7 +147,7 @@ This would then fix the missing event in the transaction context data topic and ## Stopping All Services ```console -$ docker-compose down +docker-compose down ``` ## Running the Quarkus Applications Locally @@ -156,7 +156,7 @@ Set `ADVERTISED_HOST_NAME` of the `kafka` service in _docker-compose.yaml_ to th Start all services except the `vegetables-service` and the `log-enricher`: ```console -$ docker-compose up --scale vegetables-service=0 --scale log-enricher=0 +docker-compose up --scale vegetables-service=0 --scale log-enricher=0 ``` Then start the three services via the Quarkus dev mode: diff --git a/auditlog/admin-service/.dockerignore b/auditlog/admin-service/.dockerignore index b86c7ac340..94810d006e 100644 --- a/auditlog/admin-service/.dockerignore +++ b/auditlog/admin-service/.dockerignore @@ -1,4 +1,5 @@ * !target/*-runner !target/*-runner.jar -!target/lib/* \ No newline at end of file +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/auditlog/admin-service/pom.xml b/auditlog/admin-service/pom.xml index 6e98e4227c..cca47f0c73 100644 --- a/auditlog/admin-service/pom.xml +++ b/auditlog/admin-service/pom.xml @@ -1,39 +1,65 @@ - - + + 4.0.0 io.debezium.demos.auditing auditing-admin-service 1.0-SNAPSHOT - 1.8 - 1.8 - UTF-8 UTF-8 - 1.4.2.Final - 2.22.0 + UTF-8 + 21 + 21 + 3.15.1 + 3.2.5 + org.kie.kogito + kogito-quarkus-bom + 10.1.0 + - io.quarkus - quarkus-universe-bom + io.quarkus.platform + quarkus-bom ${quarkus.version} pom import + + ${kogito.bom.group-id} + ${kogito.bom.artifact-id} + ${kogito.bom.version} + pom + import + + + + org.kie.kogito + kogito-api + + + io.quarkus + quarkus-messaging-kafka + - org.kie.kogito - kogito-quarkus + io.quarkus + quarkus-rest io.quarkus - quarkus-smallrye-reactive-messaging-kafka + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + @@ -52,13 +78,15 @@ maven-surefire-plugin ${surefire-plugin.version} - + org.jboss.logmanager.LogManager - + ${maven.home} + + native @@ -69,21 +97,6 @@ - - io.quarkus - quarkus-maven-plugin - ${quarkus.version} - - - - native-image - - - true - - - - maven-failsafe-plugin ${surefire-plugin.version} @@ -94,9 +107,11 @@ verify - + ${project.build.directory}/${project.build.finalName}-runner - + org.jboss.logmanager.LogManager + ${maven.home} + diff --git a/auditlog/admin-service/src/main/docker/Dockerfile.jvm b/auditlog/admin-service/src/main/docker/Dockerfile.jvm index 39cbd25f9c..04e029bf9e 100644 --- a/auditlog/admin-service/src/main/docker/Dockerfile.jvm +++ b/auditlog/admin-service/src/main/docker/Dockerfile.jvm @@ -14,10 +14,20 @@ # docker run -i --rm -p 8080:8080 quarkus/admin-service-jvm # ### -# FROM fabric8/java-alpine-openjdk8-jre -FROM fabric8/java-centos-openjdk8-jdk -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" -ENV AB_ENABLED=jmx_exporter -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file +FROM registry.access.redhat.com/ubi8/openjdk-21:1.23 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] \ No newline at end of file diff --git a/auditlog/admin-service/src/main/docker/Dockerfile.native b/auditlog/admin-service/src/main/docker/Dockerfile.native index 567f42f7a4..e3223b6ab1 100644 --- a/auditlog/admin-service/src/main/docker/Dockerfile.native +++ b/auditlog/admin-service/src/main/docker/Dockerfile.native @@ -14,9 +14,14 @@ # docker run -i --rm -p 8080:8080 quarkus/admin-service # ### -FROM registry.access.redhat.com/ubi8/ubi-minimal +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 WORKDIR /work/ -COPY target/*-runner /work/application -RUN chmod 775 /work +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + EXPOSE 8080 -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/service/AdminService.java b/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/service/AdminService.java index a305604d43..dbda325694 100644 --- a/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/service/AdminService.java +++ b/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/service/AdminService.java @@ -1,54 +1,44 @@ package io.debezium.demos.auditing.admin.service; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; - +import org.kie.api.KieServices; +import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; -import org.kie.kogito.rules.KieRuntimeBuilder; import io.debezium.demos.auditing.admin.TransactionEvent; import io.debezium.demos.auditing.admin.VegetableEvent; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class AdminService { - @Inject - KieRuntimeBuilder kruntimeBuilder; - - private KieSession workinMemory; - + private KieContainer kContainer; + @PostConstruct public void setup() { - - this.workinMemory = kruntimeBuilder.newKieSession(); - } - - @PreDestroy - public void cleanup() { - this.workinMemory.dispose(); + KieServices kieServices = KieServices.Factory.get(); + this.kContainer = kieServices.getKieClasspathContainer(); } - - public TransactionEvent processTransaction(TransactionEvent event) { - if (event == null) { - throw new RuntimeException("Missing transaction event"); + + public void processEvents(TransactionEvent tx, VegetableEvent veg) { + if (tx == null || veg == null) { + throw new IllegalArgumentException("Both TransactionEvent and VegetableEvent are required"); } - - workinMemory.insert(event); - - workinMemory.fireAllRules(); - return event; - } - public VegetableEvent processVegetable(VegetableEvent event) { - if (event == null) { - throw new RuntimeException("Missing transaction event"); + KieSession session = null; + + try { + session = kContainer.newKieSession(); + + session.insert(tx); + session.insert(veg); + + session.fireAllRules(); + + } finally { + if (session != null) { + session.dispose(); + } } - - workinMemory.insert(event); - - workinMemory.fireAllRules(); - return event; } -} +} \ No newline at end of file diff --git a/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/wih/SendTransactionEventWIHandler.java b/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/wih/SendTransactionEventWIHandler.java index 21340a6d45..c358852fe2 100644 --- a/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/wih/SendTransactionEventWIHandler.java +++ b/auditlog/admin-service/src/main/java/io/debezium/demos/auditing/admin/wih/SendTransactionEventWIHandler.java @@ -2,8 +2,6 @@ import java.util.Collections; -import javax.enterprise.context.ApplicationScoped; - import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; import org.kie.api.runtime.process.WorkItem; @@ -17,14 +15,17 @@ import io.debezium.demos.auditing.admin.TransactionEvent; import io.debezium.demos.auditing.admin.VegetableEvent; import io.smallrye.reactive.messaging.kafka.KafkaRecord; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; @ApplicationScoped public class SendTransactionEventWIHandler implements WorkItemHandler { - private ObjectMapper json = new ObjectMapper(); + @Inject + ObjectMapper json; @Channel("missingtransactions") - Emitter emitter; + Emitter> emitter; @Override public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { @@ -32,38 +33,52 @@ public void executeWorkItem(WorkItem workItem, WorkItemManager manager) { VegetableEvent vegetable = (VegetableEvent) workItem.getParameter("vegetable"); AuditData audit = (AuditData) workItem.getParameter("audit"); - TransactionEvent event = new TransactionEvent(); - - event.setSource(vegetable.getSource()); - event.setOperation(vegetable.getOperation()); - event.setTimestamp(new java.util.Date()); + if (vegetable == null || audit == null) { + throw new IllegalArgumentException("Missing required parameters: vegetable or audit"); + } - TransactionData data = new TransactionData(); - data.setTransactionId(vegetable.getSource().getTransactionId()); - data.setUseCase(audit.getUseCase()); - data.setClientDate(new java.util.Date()); - data.setUsername(audit.getUsername()); + TransactionEvent event = buildEvent(vegetable, audit); - event.setAfter(data); + String key = json.writeValueAsString( + Collections.singletonMap("transaction_id", vegetable.getSource().getTransactionId())); - String key = json.writeValueAsString(Collections.singletonMap("transaction_id", vegetable.getSource().getTransactionId())); String value = json.writeValueAsString(event); - emitter.send(KafkaRecord.of(key, value)); + emitter.send(KafkaRecord.of(key, value)) + .toCompletableFuture() + .exceptionally(ex -> { + throw new RuntimeException("Failed to send Kafka message", ex); + }); manager.completeWorkItem(workItem.getId(), null); + } catch (Exception e) { + manager.abortWorkItem(workItem.getId()); throw new RuntimeException(e); } } - @Override - public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { + private TransactionEvent buildEvent(VegetableEvent vegetable, AuditData audit) { + TransactionEvent event = new TransactionEvent(); + + event.setSource(vegetable.getSource()); + event.setOperation(vegetable.getOperation()); + event.setTimestamp(new java.util.Date()); + + TransactionData data = new TransactionData(); + data.setTransactionId(vegetable.getSource().getTransactionId()); + data.setUseCase(audit.getUseCase()); + data.setClientDate(new java.util.Date()); + data.setUsername(audit.getUsername()); + + event.setAfter(data); + + return event; } @Override - public String getName() { - return "Send Task"; + public void abortWorkItem(WorkItem workItem, WorkItemManager manager) { + manager.abortWorkItem(workItem.getId()); } -} +} \ No newline at end of file diff --git a/auditlog/admin-service/src/main/resources/application.properties b/auditlog/admin-service/src/main/resources/application.properties index 56494d5f60..1269377626 100644 --- a/auditlog/admin-service/src/main/resources/application.properties +++ b/auditlog/admin-service/src/main/resources/application.properties @@ -1,22 +1,24 @@ # Configuration file # key = value - +# kafka.bootstrap.servers=localhost:9092 mp.messaging.incoming.transactions.connector=smallrye-kafka -mp.messaging.incoming.transactions.bootstrap.servers=localhost:9092 +# mp.messaging.incoming.transactions.bootstrap.servers=localhost:9092 mp.messaging.incoming.transactions.topic=dbserver1.inventory.transaction_context_data mp.messaging.incoming.transactions.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer #mp.messaging.incoming.transactions.auto.offset.reset=earliest mp.messaging.incoming.vegetables.connector=smallrye-kafka -mp.messaging.incoming.vegetables.bootstrap.servers=localhost:9092 +# mp.messaging.incoming.vegetables.bootstrap.servers=localhost:9092 mp.messaging.incoming.vegetables.topic=dbserver1.inventory.vegetable mp.messaging.incoming.vegetables.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer #mp.messaging.incoming.vegetables.auto.offset.reset=earliest mp.messaging.outgoing.missingtransactions.connector=smallrye-kafka -mp.messaging.outgoing.missingtransactions.bootstrap.servers=localhost:9092 +# mp.messaging.outgoing.missingtransactions.bootstrap.servers=localhost:9092 mp.messaging.outgoing.missingtransactions.topic=dbserver1.inventory.transaction_context_data mp.messaging.outgoing.missingtransactions.key.serializer=org.apache.kafka.common.serialization.StringSerializer mp.messaging.outgoing.missingtransactions.value.serializer=org.apache.kafka.common.serialization.StringSerializer -kogito.messaging.as-cloudevents=false \ No newline at end of file +kogito.messaging.as-cloudevents=false +# Force Dev Services to use the modern Redpanda image +quarkus.kafka.devservices.image-name=docker.io/redpandadata/redpanda:v24.1.2 \ No newline at end of file diff --git a/auditlog/docker-compose.yaml b/auditlog/docker-compose.yaml index 3677d96aee..569fcbdeed 100644 --- a/auditlog/docker-compose.yaml +++ b/auditlog/docker-compose.yaml @@ -3,42 +3,65 @@ services: image: quay.io/debezium/kafka:${DEBEZIUM_VERSION} ports: - 9092:9092 + # environment: + # KAFKA_NODE_ID: 1 + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT" + # KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT_HOST://localhost:9092,PLAINTEXT://broker:19092" + # KAFKA_PROCESS_ROLES: "broker,controller" + # # KAFKA_CONTROLLER_QUORUM_VOTERS: "1@broker:29093" + # + # KAFKA_LISTENERS: "CONTROLLER://:29093,PLAINTEXT_HOST://:9092,PLAINTEXT://:19092" + # KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT" + # KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER" + # CLUSTER_ID: "4L6g3nShT-eMCtK--X86sw" + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + # KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + # KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + # KAFKA_SHARE_COORDINATOR_STATE_TOPIC_REPLICATION_FACTOR: 1 + # KAFKA_SHARE_COORDINATOR_STATE_TOPIC_MIN_ISR: 1 + # KAFKA_LOG_DIRS: "/tmp/kraft-combined-logs" - 9093:9093 + - 29092:29092 environment: - CLUSTER_ID=oh-sxaDRTcyAr6pFRbXyzA - - NODE_ID=1 - - NODE_ROLE=combined - - KAFKA_CONTROLLER_QUORUM_VOTERS=1@kafka:9093 - - KAFKA_LISTENERS=PLAINTEXT://kafka:9092,CONTROLLER://kafka:9093 - - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092 + - KAFKA_NODE_ID=1 + - KAFKA_PROCESS_ROLES=broker,controller + - KAFKA_CONTROLLER_QUORUM_BOOTSTRAP_SERVERS=kafka:9093 + - KAFKA_LISTENERS=PLAINTEXT://kafka:9092,CONTROLLER://kafka:9093,DEVLISTENER://localhost:29092 + - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,DEVLISTENER://localhost:29092 + - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=PLAINTEXT:PLAINTEXT,DEVLISTENER:PLAINTEXT,CONTROLLER:PLAINTEXT # For local development of auditlog-enricher # - ADVERTISED_HOST_NAME=192.168.1.6 - KAFKA_GROUP_MIN_SESSION_TIMEOUT_MS=250 + - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 + - KAFKA_DEFAULT_REPLICATION_FACTOR=1 + - KAFKA_MIN_INSYNC_REPLICAS=1 vegetables-db: image: quay.io/debezium/example-postgres:${DEBEZIUM_VERSION} ports: - - 5432:5432 + - 5432:5432 environment: - - POSTGRES_USER=postgresuser - - POSTGRES_PASSWORD=postgrespw - - POSTGRES_DB=vegetablesdb + - POSTGRES_USER=postgresuser + - POSTGRES_PASSWORD=postgrespw + - POSTGRES_DB=vegetablesdb connect: image: quay.io/debezium/connect:${DEBEZIUM_VERSION} ports: - - 8083:8083 + - 8083:8083 depends_on: - - kafka - - vegetables-db + - kafka + - vegetables-db environment: - - BOOTSTRAP_SERVERS=kafka:9092 - - GROUP_ID=1 - - CONFIG_STORAGE_TOPIC=my_source_connect_configs - - OFFSET_STORAGE_TOPIC=my_source_connect_offsets - - STATUS_STORAGE_TOPIC=my_source_connect_statuses - - CONNECT_KEY_CONVERTER_SCHEMAS_ENABLE=false - - CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE=false + - BOOTSTRAP_SERVERS=kafka:9092 + - GROUP_ID=1 + - CONFIG_STORAGE_TOPIC=my_source_connect_configs + - OFFSET_STORAGE_TOPIC=my_source_connect_offsets + - STATUS_STORAGE_TOPIC=my_source_connect_statuses + - CONNECT_KEY_CONVERTER_SCHEMAS_ENABLE=false + - CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE=false vegetables-service: image: debezium-examples/auditing-vegetables-service:${DEBEZIUM_VERSION} @@ -46,14 +69,11 @@ services: context: vegetables-service dockerfile: src/main/docker/Dockerfile.jvm ports: - - 8080:8080 + - 8080:8080 depends_on: - - vegetables-db + - vegetables-db environment: - - QUARKUS_DATASOURCE_URL=jdbc:postgresql://vegetables-db:5432/vegetablesdb?currentSchema=inventory - #depends_on: - # vegetable-db: - # condition: service_healthy + - QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://vegetables-db:5432/vegetablesdb?currentSchema=inventory log-enricher: image: debezium-examples/auditing-log-enricher:${DEBEZIUM_VERSION} @@ -61,11 +81,11 @@ services: context: log-enricher dockerfile: src/main/docker/Dockerfile.jvm ports: - - 8081:8080 + - 8081:8080 depends_on: - - kafka + - kafka environment: - - QUARKUS_KAFKA_STREAMS_BOOTSTRAP_SERVERS=kafka:9092 + - QUARKUS_KAFKA_STREAMS_BOOTSTRAP_SERVERS=kafka:9092 admin-service: image: debezium-examples/auditing-admin-service:${DEBEZIUM_VERSION} @@ -73,10 +93,11 @@ services: context: admin-service dockerfile: src/main/docker/Dockerfile.jvm ports: - - 8085:8080 + - 8085:8080 depends_on: - - kafka + - kafka environment: - - MP_MESSAGING_INCOMING_VEGETABLES_BOOTSTRAP_SERVERS=kafka:9092 - - MP_MESSAGING_INCOMING_TRANSACTIONS_BOOTSTRAP_SERVERS=kafka:9092 - - MP_MESSAGING_OUTGOING_MISSINGTRANSACTIONS_BOOTSTRAP_SERVERS=kafka:9092 + - KAFKA_BOOTSTRAP_SERVERS=kafka:9092 + - MP_MESSAGING_INCOMING_VEGETABLES_BOOTSTRAP_SERVERS=kafka:9092 + - MP_MESSAGING_INCOMING_TRANSACTIONS_BOOTSTRAP_SERVERS=kafka:9092 + - MP_MESSAGING_OUTGOING_MISSINGTRANSACTIONS_BOOTSTRAP_SERVERS=kafka:9092 diff --git a/auditlog/log-enricher/.dockerignore b/auditlog/log-enricher/.dockerignore index b86c7ac340..94810d006e 100644 --- a/auditlog/log-enricher/.dockerignore +++ b/auditlog/log-enricher/.dockerignore @@ -1,4 +1,5 @@ * !target/*-runner !target/*-runner.jar -!target/lib/* \ No newline at end of file +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/auditlog/log-enricher/pom.xml b/auditlog/log-enricher/pom.xml index 92eb40418a..6214abed3b 100644 --- a/auditlog/log-enricher/pom.xml +++ b/auditlog/log-enricher/pom.xml @@ -7,16 +7,16 @@ 1.0-SNAPSHOT UTF-8 - 2.22.0 - 1.4.2.Final UTF-8 - 1.8 - 1.8 + 21 + 21 + 3.15.1 + 3.2.5 - io.quarkus + io.quarkus.platform quarkus-bom ${quarkus.version} pom @@ -27,17 +27,11 @@ io.quarkus - quarkus-resteasy + quarkus-rest io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test + quarkus-jsonp io.quarkus @@ -45,7 +39,13 @@ io.quarkus - quarkus-jsonp + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test @@ -66,57 +66,12 @@ maven-surefire-plugin ${surefire-plugin.version} - + org.jboss.logmanager.LogManager - + ${maven.home} + - - - native - - - native - - - - - - io.quarkus - quarkus-maven-plugin - ${quarkus.version} - - - - native-image - - - true - - - - - - maven-failsafe-plugin - ${surefire-plugin.version} - - - - integration-test - verify - - - - ${project.build.directory}/${project.build.finalName}-runner - - - - - - - - - - + \ No newline at end of file diff --git a/auditlog/log-enricher/src/main/docker/Dockerfile.jvm b/auditlog/log-enricher/src/main/docker/Dockerfile.jvm index 4bb95f5ec9..17f3ef0187 100644 --- a/auditlog/log-enricher/src/main/docker/Dockerfile.jvm +++ b/auditlog/log-enricher/src/main/docker/Dockerfile.jvm @@ -14,9 +14,20 @@ # docker run -i --rm -p 8080:8080 quarkus/auditing-log-enricher-jvm # ### -FROM fabric8/java-centos-openjdk8-jdk -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" -ENV AB_ENABLED=jmx_exporter -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file +FROM registry.access.redhat.com/ubi8/openjdk-21:1.23 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] \ No newline at end of file diff --git a/auditlog/log-enricher/src/main/docker/Dockerfile.native b/auditlog/log-enricher/src/main/docker/Dockerfile.native index 68474bdffc..630f9db631 100644 --- a/auditlog/log-enricher/src/main/docker/Dockerfile.native +++ b/auditlog/log-enricher/src/main/docker/Dockerfile.native @@ -14,9 +14,14 @@ # docker run -i --rm -p 8080:8080 quarkus/auditing-log-enricher # ### -FROM registry.access.redhat.com/ubi8/ubi-minimal +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 WORKDIR /work/ -COPY target/*-runner /work/application -RUN chmod 775 /work +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + EXPOSE 8080 -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/BufferOffsets.java b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/BufferOffsets.java index e9f14d66ab..beea245b84 100644 --- a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/BufferOffsets.java +++ b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/BufferOffsets.java @@ -1,7 +1,7 @@ package io.debezium.demos.auditing.enricher; -import javax.json.Json; -import javax.json.JsonObject; +import jakarta.json.Json; +import jakarta.json.JsonObject; /** * Keeps track of the position within the key/value state store that is used as @@ -31,8 +31,7 @@ public static BufferOffsets initial() { static BufferOffsets fromJson(JsonObject json) { return new BufferOffsets( json.getJsonNumber("firstValue").longValue(), - json.getJsonNumber("nextValue").longValue() - ); + json.getJsonNumber("nextValue").longValue()); } public JsonObject toJson() { diff --git a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/ChangeEventEnricher.java b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/ChangeEventEnricher.java index 962f185f0a..b465ca5446 100644 --- a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/ChangeEventEnricher.java +++ b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/ChangeEventEnricher.java @@ -4,9 +4,6 @@ import java.util.Arrays; import java.util.Optional; -import javax.json.Json; -import javax.json.JsonObject; - import org.apache.kafka.streams.KeyValue; import org.apache.kafka.streams.kstream.Transformer; import org.apache.kafka.streams.processor.ProcessorContext; @@ -17,6 +14,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.json.Json; +import jakarta.json.JsonObject; + /** * Enriches change events with transaction-scoped metadata. If no metadata for * the associated transaction can be retrieved yet (as the change event gets @@ -39,7 +39,8 @@ class ChangeEventEnricher implements Transformer) context.getStateStore(TopologyProducer.STREAM_BUFFER_NAME); - txMetaDataStore = (TimestampedKeyValueStore) context.getStateStore(TopologyProducer.STORE_NAME); + txMetaDataStore = (TimestampedKeyValueStore) context + .getStateStore(TopologyProducer.STORE_NAME); context.schedule(Duration.ofSeconds(1), PunctuationType.WALL_CLOCK_TIME, ts -> enrichAndEmitBufferedEvents()); } @@ -79,12 +80,13 @@ private boolean enrichAndEmitBufferedEvents() { boolean enrichedAllBuffered = true; - for(long i = sequence.getFirstValue(); i < sequence.getNextValue(); i++) { + for (long i = sequence.getFirstValue(); i < sequence.getNextValue(); i++) { JsonObject buffered = streamBuffer.get(i); LOG.info("Processing buffered change event for key {}", buffered.getJsonObject("key")); - KeyValue enriched = enrichWithTxMetaData(buffered.getJsonObject("key"), buffered.getJsonObject("changeEvent")); + KeyValue enriched = enrichWithTxMetaData(buffered.getJsonObject("key"), + buffered.getJsonObject("changeEvent")); if (enriched == null) { enrichedAllBuffered = false; break; @@ -117,8 +119,7 @@ private void bufferChangeEvent(JsonObject key, JsonObject changeEvent) { streamBuffer.putAll(Arrays.asList( KeyValue.pair(sequence.getNextValueAndIncrement(), wrapper), - KeyValue.pair(BUFFER_OFFSETS_KEY, sequence.toJson()) - )); + KeyValue.pair(BUFFER_OFFSETS_KEY, sequence.toJson()))); } /** @@ -145,9 +146,8 @@ private KeyValue enrichWithTxMetaData(JsonObject key, Js return KeyValue.pair( key, Json.createObjectBuilder(changeEvent) - .add("audit", txMetaData) - .build() - ); + .add("audit", txMetaData) + .build()); } LOG.warn("No metadata found for transaction {}", txId); @@ -158,8 +158,7 @@ private Optional bufferOffsets() { JsonObject bufferOffsets = streamBuffer.get(BUFFER_OFFSETS_KEY); if (bufferOffsets == null) { return Optional.empty(); - } - else { + } else { return Optional.of(BufferOffsets.fromJson(bufferOffsets)); } } diff --git a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/JsonObjectSerde.java b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/JsonObjectSerde.java index 92c03ba66a..e706207b9d 100644 --- a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/JsonObjectSerde.java +++ b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/JsonObjectSerde.java @@ -5,15 +5,14 @@ import java.io.IOException; import java.util.Map; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; - import org.apache.kafka.common.serialization.Deserializer; import org.apache.kafka.common.serialization.Serde; import org.apache.kafka.common.serialization.Serializer; import io.quarkus.runtime.annotations.RegisterForReflection; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; /** * A {@link Serde} that (de-)serializes JSON. diff --git a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/TopologyProducer.java b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/TopologyProducer.java index 9154948d1f..1eb62d8f2e 100644 --- a/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/TopologyProducer.java +++ b/auditlog/log-enricher/src/main/java/io/debezium/demos/auditing/enricher/TopologyProducer.java @@ -1,9 +1,5 @@ package io.debezium.demos.auditing.enricher; -import javax.enterprise.context.ApplicationScoped; -import javax.enterprise.inject.Produces; -import javax.json.JsonObject; - import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.streams.StreamsBuilder; import org.apache.kafka.streams.Topology; @@ -13,6 +9,10 @@ import org.apache.kafka.streams.state.Stores; import org.eclipse.microprofile.config.inject.ConfigProperty; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.json.JsonObject; + @ApplicationScoped public class TopologyProducer { @@ -32,15 +32,13 @@ public class TopologyProducer { public Topology buildTopology() { StreamsBuilder builder = new StreamsBuilder(); - StoreBuilder> streamBufferStateStore = - Stores - .keyValueStoreBuilder( + StoreBuilder> streamBufferStateStore = Stores + .keyValueStoreBuilder( Stores.persistentKeyValueStore(STREAM_BUFFER_NAME), new Serdes.LongSerde(), - new JsonObjectSerde() - ) - .withCachingDisabled(); - builder.addStateStore(streamBufferStateStore); + new JsonObjectSerde()) + .withCachingDisabled(); + builder.addStateStore(streamBufferStateStore); builder.globalTable(txContextDataTopic, Materialized.as(STORE_NAME)); @@ -50,7 +48,8 @@ public Topology buildTopology() { .filter((id, changeEvent) -> changeEvent != null) // exclude snapshot events .filter((id, changeEvent) -> !changeEvent.getString("op").equals("r")) - // enrich change events with transaction metadata via the statestore of the TX topic + // enrich change events with transaction metadata via the statestore of the TX + // topic .transform(() -> new ChangeEventEnricher(), STREAM_BUFFER_NAME) .to(vegetablesEnrichedTopic); diff --git a/auditlog/register-postgres.json b/auditlog/register-postgres.json index d56ab63378..96b90f1497 100644 --- a/auditlog/register-postgres.json +++ b/auditlog/register-postgres.json @@ -5,7 +5,8 @@ "database.port": "5432", "database.user": "postgresuser", "database.password": "postgrespw", - "database.dbname" : "vegetablesdb", + "database.dbname": "vegetablesdb", "database.server.name": "dbserver1", - "table.include.list": "inventory.vegetable,inventory.transaction_context_data" -} + "table.include.list": "inventory.vegetable,inventory.transaction_context_data", + "topic.prefix": "dbserver1" +} \ No newline at end of file diff --git a/auditlog/vegetables-service/pom.xml b/auditlog/vegetables-service/pom.xml index 8e64ffc332..eecfa611d6 100644 --- a/auditlog/vegetables-service/pom.xml +++ b/auditlog/vegetables-service/pom.xml @@ -7,16 +7,16 @@ 1.0-SNAPSHOT UTF-8 - 2.22.0 - 1.4.2.Final UTF-8 - 1.8 - 1.8 + 21 + 21 + 3.15.1 + 3.2.5 - io.quarkus + io.quarkus.platform quarkus-bom ${quarkus.version} pom @@ -27,30 +27,22 @@ io.quarkus - quarkus-resteasy + quarkus-rest io.quarkus - quarkus-undertow + quarkus-rest-jsonb + io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test + quarkus-undertow + io.quarkus quarkus-smallrye-jwt - - io.quarkus - quarkus-resteasy-jsonb - io.quarkus quarkus-hibernate-orm @@ -63,6 +55,17 @@ io.quarkus quarkus-jdbc-postgresql + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + @@ -82,9 +85,10 @@ maven-surefire-plugin ${surefire-plugin.version} - + org.jboss.logmanager.LogManager - + ${maven.home} + @@ -99,21 +103,6 @@ - - io.quarkus - quarkus-maven-plugin - ${quarkus.version} - - - - native-image - - - true - - - - maven-failsafe-plugin ${surefire-plugin.version} @@ -124,9 +113,11 @@ verify - + ${project.build.directory}/${project.build.finalName}-runner - + org.jboss.logmanager.LogManager + ${maven.home} + @@ -135,4 +126,4 @@ - + \ No newline at end of file diff --git a/auditlog/vegetables-service/src/main/docker/Dockerfile.jvm b/auditlog/vegetables-service/src/main/docker/Dockerfile.jvm index 833541894e..38d0c8cdad 100644 --- a/auditlog/vegetables-service/src/main/docker/Dockerfile.jvm +++ b/auditlog/vegetables-service/src/main/docker/Dockerfile.jvm @@ -14,10 +14,20 @@ # docker run -i --rm -p 8080:8080 quarkus/debezium-auditing-demo-jvm # ### -# FROM fabric8/java-alpine-openjdk8-jre -FROM fabric8/java-centos-openjdk8-jdk -ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" -ENV AB_ENABLED=jmx_exporter -COPY target/lib/* /deployments/lib/ -COPY target/*-runner.jar /deployments/app.jar -ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file +FROM registry.access.redhat.com/ubi8/openjdk-21:1.23 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] \ No newline at end of file diff --git a/auditlog/vegetables-service/src/main/docker/Dockerfile.native b/auditlog/vegetables-service/src/main/docker/Dockerfile.native index 4caf3be3b4..9e54ef2e87 100644 --- a/auditlog/vegetables-service/src/main/docker/Dockerfile.native +++ b/auditlog/vegetables-service/src/main/docker/Dockerfile.native @@ -14,9 +14,14 @@ # docker run -i --rm -p 8080:8080 quarkus/debezium-auditing-demo # ### -FROM registry.access.redhat.com/ubi8/ubi-minimal +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.10 WORKDIR /work/ -COPY target/*-runner /work/application -RUN chmod 775 /work +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 target/*-runner /work/application + EXPOSE 8080 -CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/model/Vegetable.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/model/Vegetable.java index 564b6f9724..1c81488974 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/model/Vegetable.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/model/Vegetable.java @@ -1,10 +1,10 @@ package io.debezium.demos.auditing.vegetables.model; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.SequenceGenerator; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; @Entity public class Vegetable { @@ -14,11 +14,7 @@ public class Vegetable { private String description; @Id - @SequenceGenerator( - name = "vegetablesSequence", - sequenceName = "vegetables_id_seq", - allocationSize = 10, - initialValue = 10) + @SequenceGenerator(name = "vegetablesSequence", sequenceName = "vegetables_id_seq", allocationSize = 10, initialValue = 10) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "vegetablesSequence") public Long getId() { return id; diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/VegetableResource.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/VegetableResource.java index 8bdfd870d3..7173058184 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/VegetableResource.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/VegetableResource.java @@ -1,24 +1,22 @@ package io.debezium.demos.auditing.vegetables.rest; -import javax.annotation.security.RolesAllowed; -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.transaction.Transactional; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -import org.jboss.resteasy.annotations.jaxrs.PathParam; - import io.debezium.demos.auditing.vegetables.model.Vegetable; import io.debezium.demos.auditing.vegetables.service.VegetableService; import io.debezium.demos.auditing.vegetables.transactioncontext.Audited; +import jakarta.annotation.security.RolesAllowed; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +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.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; @Path("/vegetables") @RequestScoped @@ -30,9 +28,9 @@ public class VegetableResource { VegetableService vegetableService; @POST - @RolesAllowed({"farmers"}) + @RolesAllowed({ "farmers" }) @Transactional - @Audited(useCase="CREATE VEGETABLE") + @Audited(useCase = "CREATE VEGETABLE") public Response createVegetable(Vegetable vegetable) { if (vegetable.getId() != null) { return Response.status(Status.BAD_REQUEST.getStatusCode()).build(); @@ -45,9 +43,9 @@ public Response createVegetable(Vegetable vegetable) { @Path("/{id}") @PUT - @RolesAllowed({"farmers"}) + @RolesAllowed({ "farmers" }) @Transactional - @Audited(useCase="UPDATE VEGETABLE") + @Audited(useCase = "UPDATE VEGETABLE") public Vegetable updateVegetable(@PathParam("id") long id, Vegetable vegetable) { vegetable.setId(id); vegetable = vegetableService.updateVegetable(vegetable); @@ -57,9 +55,9 @@ public Vegetable updateVegetable(@PathParam("id") long id, Vegetable vegetable) @Path("/{id}") @DELETE - @RolesAllowed({"farmers"}) + @RolesAllowed({ "farmers" }) @Transactional - @Audited(useCase="DELETE VEGETABLE") + @Audited(useCase = "DELETE VEGETABLE") public void deleteVegetable(@PathParam("id") long id) { vegetableService.deleteVegetable(id); } diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/util/ErrorMapper.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/util/ErrorMapper.java index 7411558748..fc5a20c5c1 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/util/ErrorMapper.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/rest/util/ErrorMapper.java @@ -1,11 +1,11 @@ package io.debezium.demos.auditing.vegetables.rest.util; -import javax.json.Json; -import javax.persistence.EntityNotFoundException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.json.Json; +import jakarta.persistence.EntityNotFoundException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; @Provider public class ErrorMapper implements ExceptionMapper { diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/service/VegetableService.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/service/VegetableService.java index 47dc696c6c..f5a687f1eb 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/service/VegetableService.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/service/VegetableService.java @@ -1,10 +1,9 @@ package io.debezium.demos.auditing.vegetables.service; -import javax.enterprise.context.ApplicationScoped; -import javax.inject.Inject; -import javax.persistence.EntityManager; - import io.debezium.demos.auditing.vegetables.model.Vegetable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; @ApplicationScoped public class VegetableService { diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/Audited.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/Audited.java index 680fccc65c..58ef7b46d7 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/Audited.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/Audited.java @@ -7,12 +7,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import javax.enterprise.util.Nonbinding; -import javax.interceptor.InterceptorBinding; +import jakarta.enterprise.util.Nonbinding; +import jakarta.interceptor.InterceptorBinding; @InterceptorBinding -@Target({METHOD, TYPE}) +@Target({ METHOD, TYPE }) @Retention(RUNTIME) public @interface Audited { - @Nonbinding String useCase(); + @Nonbinding + String useCase(); } diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionContextData.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionContextData.java index 514d858988..8887995f22 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionContextData.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionContextData.java @@ -2,25 +2,25 @@ import java.time.ZonedDateTime; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; @Entity -@Table(name="transaction_context_data") +@Table(name = "transaction_context_data") public class TransactionContextData { @Id - @Column(name="transaction_id") + @Column(name = "transaction_id") public long transactionId; - @Column(name="user_name") + @Column(name = "user_name") public String userName; - @Column(name="client_date") + @Column(name = "client_date") public ZonedDateTime clientDate; - @Column(name="useCase") + @Column(name = "useCase") public String useCase; } diff --git a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionInterceptor.java b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionInterceptor.java index bd647dd90c..5a001539de 100644 --- a/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionInterceptor.java +++ b/auditlog/vegetables-service/src/main/java/io/debezium/demos/auditing/vegetables/transactioncontext/TransactionInterceptor.java @@ -1,20 +1,19 @@ package io.debezium.demos.auditing.vegetables.transactioncontext; -import java.math.BigInteger; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import javax.annotation.Priority; -import javax.inject.Inject; -import javax.interceptor.AroundInvoke; -import javax.interceptor.Interceptor; -import javax.interceptor.InvocationContext; -import javax.persistence.EntityManager; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.HttpHeaders; - import org.eclipse.microprofile.jwt.JsonWebToken; +import jakarta.annotation.Priority; +import jakarta.inject.Inject; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.Interceptor; +import jakarta.interceptor.InvocationContext; +import jakarta.persistence.EntityManager; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.HttpHeaders; + @Interceptor @Priority(value = Interceptor.Priority.APPLICATION + 100) @Audited(useCase = "") @@ -27,16 +26,16 @@ public class TransactionInterceptor { EntityManager entityManager; @Inject - HttpServletRequest request; + ContainerRequestContext requestContext; @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { - BigInteger txtId = (BigInteger) entityManager.createNativeQuery("SELECT txid_current()").getSingleResult(); + Long txtId = (Long) entityManager.createNativeQuery("SELECT txid_current()").getSingleResult(); String useCase = ctx.getMethod().getAnnotation(Audited.class).useCase(); TransactionContextData context = new TransactionContextData(); - context.transactionId = txtId.longValueExact(); + context.transactionId = txtId; context.userName = jwt.claim("sub").orElse("anonymous"); context.clientDate = getRequestDate(); context.useCase = useCase; @@ -47,7 +46,7 @@ public Object manageTransaction(InvocationContext ctx) throws Exception { } private ZonedDateTime getRequestDate() { - String requestDate = request.getHeader(HttpHeaders.DATE); + String requestDate = requestContext.getHeaderString(HttpHeaders.DATE); return requestDate != null ? ZonedDateTime.parse(requestDate, DateTimeFormatter.RFC_1123_DATE_TIME) : null; } } diff --git a/auditlog/vegetables-service/src/main/resources/application.properties b/auditlog/vegetables-service/src/main/resources/application.properties index 0703c7bcdb..f8b67a7e96 100644 --- a/auditlog/vegetables-service/src/main/resources/application.properties +++ b/auditlog/vegetables-service/src/main/resources/application.properties @@ -1,12 +1,13 @@ mp.jwt.verify.issuer=farmshop mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjYWJ6Zt9Jo9dxVuMglo0rYN4vBV0T7AP+qD/aI7tTrus6ZMvTi/+JKlNpEAS0b6yasYjxuKmh3eYT0PbGmGERr07VDsVcV/iezl9pj+fceY4lebrExS36yGQJs6BUXYF4P8ynmvnKC40AuyxKFgb3T08h1jxoBsBKlPfAT620ZP1vwgGwZB7iAfzdNYtt3z2NtkyPMaD1mHU6rxewjVN9XVSSSPKO8nFPTYsm1i4ePohgWr9bxwFHkXzyk7DnpUBMZzlVUUXVPuEpkVCqnWZTslMw/pgsyXPw1pmV76rVwhI0Ay4XohPW2QvDoPKHhuiQtcNrfL++iEFG8A9hh1K3QIDAQAB -quarkus.datasource.url=jdbc:postgresql://localhost:5432/vegetablesdb?currentSchema=inventory -quarkus.datasource.driver=org.postgresql.Driver +quarkus.datasource.db-kind=postgresql +quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/vegetablesdb?currentSchema=inventory + quarkus.datasource.username=postgresuser quarkus.datasource.password=postgrespw -quarkus.datasource.max-size=8 -quarkus.datasource.min-size=2 +quarkus.datasource.jdbc.max-size=8 +quarkus.datasource.jdbc.min-size=2 quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.log.sql=true quarkus.hibernate-orm.jdbc.timezone=UTC