diff --git a/.gitignore b/.gitignore index 5db4ebf6..cae78014 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ code/appinfo META-INF **.iml .hugo_build.lock -target \ No newline at end of file +target +/.quarkus +/diff-update diff --git a/assets/css/dep.css b/assets/css/dep.css new file mode 100644 index 00000000..622da7b6 --- /dev/null +++ b/assets/css/dep.css @@ -0,0 +1,27 @@ +table.dependencies { + font-size: 0.8em; + margin-bottom: 15px; + border-collapse: collapse; + padding: 0; + width: 100%; +} + +table.dependencies caption { + font-size: 1.5em; + color: #1E5A96; + margin: 0.5em 0 0.75em; +} + +table.dependencies tr { + padding: 0.35em; +} + +table.dependencies td, +table.dependencies th { + padding: 5px 15px 5px 15px; +} + +table.dependencies th { + font-weight: bold; + text-transform: uppercase; +} \ No newline at end of file diff --git a/config/_default/config.toml b/config/_default/config.toml index bb4ede3d..4c03615f 100644 --- a/config/_default/config.toml +++ b/config/_default/config.toml @@ -78,14 +78,14 @@ lab_code_basedir = "/code/" solution_code_basedir = "/solution/" #versions -quarkusVersion = "3.10.0" +quarkusVersion = "3.11.1" mutinyVersion = "2.5.1" -confluentKafkaAvroVersion = "7.5.1" -openJdkImage = "registry.access.redhat.com/ubi8/openjdk-17:1.16" -ubiQuarkusNativeImage = "quay.io/quarkus/ubi-quarkus-native-image:22.3-java17" +confluentKafkaAvroVersion = "7.6.1" +openJdkImage = "registry.access.redhat.com/ubi8/openjdk-21:1.19" +ubiQuarkusNativeImage = "quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21" quarkusMicroImage = "quay.io/quarkus/quarkus-micro-image:2.0" -strimziVersion = "quay.io/strimzi/kafka:0.37.0-kafka-3.5.1" -jaegerTracingImage = " quay.io/jaegertracing/all-in-one:1.50.0" +strimziVersion = "quay.io/strimzi/kafka:0.41.0-kafka-3.7.0" +jaegerTracingImage = " quay.io/jaegertracing/all-in-one:1.57.0" grafanaImage = "docker.io/grafana/grafana:latest" prometheusImage = "quay.io/prometheus/prometheus:latest" diff --git a/content/en/docs/01.0/12_quarkus.md b/content/en/docs/01.0/12_quarkus.md index 40480bc8..958f768e 100644 --- a/content/en/docs/01.0/12_quarkus.md +++ b/content/en/docs/01.0/12_quarkus.md @@ -148,12 +148,18 @@ public class ApplicationImpl extends Application { } static { + DisabledInitialContextManager.register(); + System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", + "io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); - System.setProperty("io.netty.allocator.maxOrder", "1"); - System.setProperty("io.netty.machineId", "e2:dd:dc:62:56:a3:ba:f8"); - ProfileManager.setLaunchMode(LaunchMode.NORMAL); + System.setProperty("io.netty.machineId", "d5:17:d2:22:b3:dd:20:8a"); + System.setProperty("io.netty.allocator.maxOrder", "3"); + System.setProperty("logging.initial-configurator.min-level", "500"); + System.setProperty("io.quarkus.security.http.test-if-basic-auth-implicitly-required", "true"); + LaunchMode.set(LaunchMode.NORMAL); StepTiming.configureEnabled(); - Timing.staticInitStarted(); + ExecutionModeManager.staticInit(); + Timing.staticInitStarted(false); Config.ensureInitialized(); LOG = Logger.getLogger("io.quarkus.application"); StartupContext var0 = new StartupContext(); @@ -163,21 +169,14 @@ public class ApplicationImpl extends Application { StepTiming.configureStart(); ((StartupTask)(new setupLoggingStaticInit-1235809433())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask)(new ioThreadDetector-1463825589())).deploy(var0); + /* ... */ + ((StartupTask)(new ResteasyReactiveProcessor.serverSerializers1997124575())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask)(new blockingOP558072755())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.setupEndpoints615463616())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask)(new build163995889())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.setupDeployment713137389())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask)(new staticInit-1777814589())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask)(new initStatic1190120725())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask)(new generateResources-1025303321())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask)(new setupResteasyInjection2143006352())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask)(new staticInit-210558872())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.addDefaultAuthFailureHandler1048820038())).deploy(var0); StepTiming.printStepTime(var0); } catch (Throwable var2) { ApplicationStateNotification.notifyStartupFailed(var2); diff --git a/content/en/docs/01.0/13_development.md b/content/en/docs/01.0/13_development.md index 33afc9ef..ea41e304 100644 --- a/content/en/docs/01.0/13_development.md +++ b/content/en/docs/01.0/13_development.md @@ -7,7 +7,6 @@ description: > Quarkus from the development perspective --- - ## Development Mode Quarkus comes with a built-in development mode. Run your application with: @@ -66,7 +65,11 @@ jar, do not attempt to run normal devmode. Now you need to connect your local agent to the remote host, using the `remote-dev` command: ```s -./mvnw quarkus:remote-dev -Ddebug=false -Dquarkus.package.type=mutable-jar -Dquarkus.live-reload.url=http://my-remote-host:8080 -Dquarkus.live-reload.password=changeit +./mvnw quarkus:remote-dev \ + -Ddebug=false \ + -Dquarkus.package.type=mutable-jar \ + -Dquarkus.live-reload.url=http://my-remote-host:8080 \ + -Dquarkus.live-reload.password=changeit ``` Now every time you refresh the browser you should see any changes you have made locally immediately visible in the @@ -144,24 +147,9 @@ the code in the `{{% param solution_code_basedir %}}dev-services` folder. The ex provision some data and uses the `hibernate-orm-panache` implementation which we will not cover any further. However, this does not change how the devservices work. -Our pom.xml looks like this: -```xml - - - io.quarkus - quarkus-jdbc-postgresql - - - io.quarkus - quarkus-flyway - - - io.quarkus - quarkus-hibernate-orm-panache - - - -``` +The dev-services `pom.xml` contains the following dependencies: + +{{< csvtable csv="/solution/dev-services/dependencies.csv" class="dependencies" >}} In our application properties we have configured the datasource type: ```properties diff --git a/content/en/docs/01.0/14_extensions.md b/content/en/docs/01.0/14_extensions.md index a3f4379c..b78cdfa8 100644 --- a/content/en/docs/01.0/14_extensions.md +++ b/content/en/docs/01.0/14_extensions.md @@ -56,7 +56,8 @@ cannot annotate them. For this to achieve we could create a simple extension: ```s -mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create-extension -N -DgroupId=ch.puzzle.quarkustechlab -DextensionId=message-converter-as-beans -DwithoutTests +mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create-extension -N \ + -DgroupId=ch.puzzle.quarkustechlab -DextensionId=message-converter-as-beans -DwithoutTests ``` In this simple extensions we only need the deployment module. diff --git a/content/en/docs/01.0/15_vertx.md b/content/en/docs/01.0/15_vertx.md index 1d2a8eac..6c3f0a66 100644 --- a/content/en/docs/01.0/15_vertx.md +++ b/content/en/docs/01.0/15_vertx.md @@ -80,4 +80,4 @@ uni.subscribe() .with(it -> System.out.println("File content is: " + it)); ``` -To dive further into the Mutiny framework consider checking their [documentation](https://smallrye.io/smallrye-mutiny/guides). +To dive further into the Mutiny framework consider checking their [documentation](https://smallrye.io/smallrye-mutiny/latest/guides/imperative-to-reactive/). diff --git a/content/en/docs/01.0/18_solutions.md b/content/en/docs/01.0/18_solutions.md index 63f31519..b8d677fc 100644 --- a/content/en/docs/01.0/18_solutions.md +++ b/content/en/docs/01.0/18_solutions.md @@ -26,5 +26,5 @@ Chapter | Chapter Name | Solution Project 8.2 | Reactive messaging with Kafka | `quarkus-reactive-messaging-producer`, `quarkus-reactive-messaging-consumer` and `kafka-stack` 8.3 | Cloud Events | `quarkus-cloudevents-producer`, `quarkus-cloudevents-consumer` and `kafka-stack` 9.2 | Tracing with Jaeger | `quarkus-opentelemetry-jaeger` and `opentelemetry-stack` -9.3 | Metrics with micrometer | `quarkus-metrics-data-producer` and `opentelemetry-stack` +9.3 | Metrics with micrometer | `quarkus-metrics-data-producer`, `quarkus-metrics-data-consumer` and `opentelemetry-stack` 10 | Quarkus Extensions | `techlab-extension-appinfo` and `quarkus-appinfo-application` diff --git a/content/en/docs/02.0/21_first-application.md b/content/en/docs/02.0/21_first-application.md index 4b5f4063..a3590005 100644 --- a/content/en/docs/02.0/21_first-application.md +++ b/content/en/docs/02.0/21_first-application.md @@ -15,6 +15,13 @@ To create your first Quarkus application you have several possibilities: * Create your application with the Quickstart UI [code.quarkus.io](https://code.quarkus.io/) * Use IntelliJ or eclipse plugins which will assist creating projects (these are usually also based on code.quarkus.io) +### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-getting-started/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. ### Task {{% param sectionnumber %}}.1: Create your application with maven @@ -46,7 +53,7 @@ live reloading on each API call. Try hitting the API and test the curl http://localhost:8080/hello ``` -You should get the `Hello from RESTEasy Reactive` response in your console. Other RESTeasy functionalities work like they always do. +You should get the `Hello from Quarkus REST` response in your console. Other RESTeasy functionalities work like they always do. For further information on basic REST interaction with Quarkus see [Documentation](https://quarkus.io/guides/rest-json). @@ -128,13 +135,15 @@ public class ApplicationImpl extends Application { static { DisabledInitialContextManager.register(); - System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); System.setProperty("java.util.concurrent.ForkJoinPool.common.threadFactory", "io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory"); - System.setProperty("logging.initial-configurator.min-level", "500"); System.setProperty("io.netty.allocator.maxOrder", "3"); - System.setProperty("io.netty.machineId", "a2:a0:88:71:ba:49:9f:1a"); - ProfileManager.setLaunchMode(LaunchMode.NORMAL); + System.setProperty("logging.initial-configurator.min-level", "500"); + System.setProperty("io.netty.machineId", "80:90:28:d0:a0:e1:64:c8"); + System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager"); + System.setProperty("io.quarkus.security.http.test-if-basic-auth-implicitly-required", "true"); + LaunchMode.set(LaunchMode.NORMAL); StepTiming.configureEnabled(); + ExecutionModeManager.staticInit(); Timing.staticInitStarted(false); Config.ensureInitialized(); LOG = Logger.getLogger("io.quarkus.application"); @@ -143,50 +152,34 @@ public class ApplicationImpl extends Application { try { StepTiming.configureStart(); - ((StartupTask) (new MutinyProcessor.buildTimeInit521613965())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new VertxCoreProcessor.ioThreadDetector1463825589())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new BlockingOperationControlBuildStep.blockingOP558072755())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new LoggingResourceProcessor.setupLoggingStaticInit2062061316())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new VirtualThreadsProcessor.setup657958880())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new ResteasyReactiveProcessor.addDefaultAuthFailureHandler1457820534())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new SmallRyeContextPropagationProcessor.buildStatic677493008())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new NativeImageConfigBuildStep.build282698227())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new VertxProcessor.currentContextFactory166049300())).deploy(var0); - StepTiming.printStepTime(var0); - ((StartupTask) (new JacksonProcessor.jacksonSupport1959914842())).deploy(var0); + ((StartupTask)(new NativeImageConfigBuildStep.build282698227())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask) (new SyntheticBeansProcessor.initStatic1190120725())).deploy(var0); + ((StartupTask)(new VertxCoreProcessor.ioThreadDetector1463825589())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask) (new ArcProcessor.generateResources844392269())).deploy(var0); + /* ... */ + ((StartupTask)(new ResteasyReactiveProcessor.serverSerializers1997124575())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask) (new ResteasyReactiveProcessor.setupEndpoints1082683577())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.setupEndpoints615463616())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask) (new ResteasyReactiveProcessor.serverSerializers168685733())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.setupDeployment713137389())).deploy(var0); StepTiming.printStepTime(var0); - ((StartupTask) (new ResteasyReactiveProcessor.setupDeployment713137389())).deploy(var0); + ((StartupTask)(new ResteasyReactiveProcessor.addDefaultAuthFailureHandler1048820038())).deploy(var0); StepTiming.printStepTime(var0); } catch (Throwable var2) { ApplicationStateNotification.notifyStartupFailed(var2); var0.close(); - throw (Throwable) (new RuntimeException("Failed to start quarkus", var2)); + throw (Throwable)(new RuntimeException("Failed to start quarkus", var2)); } } + /* ... */ } ``` -Our application contains RESTEasy Reactive endpoints and uses different Serializers. You may find a line containing `((StartupTask) (new ResteasyReactiveProcessor.serverSerializers168685733())).deploy(var0);` in the static block. +Our application contains REST endpoints and uses different Serializers. You may find a line containing `((StartupTask)(new ResteasyReactiveProcessor.serverSerializers1997124575())).deploy(var0);` in the static block. Where does this come from? Open the file `ResteasyReactiveProcessor$serverSerializers...` from the `io/quarkus/deployment/steps` folder. You may find the deploy code for the resteasy de-/serializers. ```java // $FF: synthetic class -public class ResteasyCommonProcessor$setupResteasyInjection2143006352 implements StartupTask { +public class ResteasyReactiveProcessor$serverSerializers1997124575 implements StartupTask { /* removed for simplicity */ diff --git a/content/en/docs/02.0/22_implementing-rest.md b/content/en/docs/02.0/22_implementing-rest.md index c93d7dd9..0de055a1 100644 --- a/content/en/docs/02.0/22_implementing-rest.md +++ b/content/en/docs/02.0/22_implementing-rest.md @@ -11,12 +11,23 @@ description: > In this section we learn how microservices can communicate through REST. In this example we want to build a microservice which produces random data when it's REST interface is called. Another microservice consumes then the data and exposes -it on it's own endpoint. +it on its own endpoint. ### {{% param sectionnumber %}}.2: Producing Data -Create a new Quarkus application like shown before called `data-producer`. The application should expose a `DataResource` on the path `/data` which provides the user with a `SensorMeasurement` holding a random double when requested. +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-rest-data-producer/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + +#### Implementation + +Create a new Quarkus application like shown before called `quarkus-rest-data-producer`. The application should expose a `DataResource` on the path `/data` which provides the user with a `SensorMeasurement` holding a random double when requested. {{% details title="Hint" %}} @@ -30,7 +41,7 @@ mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ {{% /details %}} -To write better APIs and share data over our defined resources, we need the `resteasy-reactive-jackson` extension which provides us with +To write better APIs and share data over our defined resources, we need the `quarkus-rest-jackson` extension which provides us with Jackson functionalities for our REST interfaces. To add an extension to your existing Quarkus application simply use: @@ -89,14 +100,25 @@ public class DataResource { Please update or delete the generated tests which Quarkus provides when generating a project. They will not be needed any further and only have demonstration purposes. -Start up your API and test your endpoint manually. If your data-producer works, let's continue consuming the data we just provided. +Start up your API and test your endpoint manually. If your `quarkus-rest-data-producer` works, let's continue consuming the data we just provided. For more information about writing REST APIs with Quarkus see the [documentation](https://quarkus.io/guides/rest-json) ### {{% param sectionnumber %}}.3: Consuming Data -With another microservice we would like to consume the data served by our data-producer. Create another quarkus application called `data-consumer` with the follwing extensions: `rest-client-reactive-jackson`. +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-rest-data-producer/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + +#### Implementation + +With another microservice we would like to consume the data served by our `quarkus-rest-data-producer`. Create another quarkus application called `quarkus-rest-data-consumer` with the follwing extensions: `quarkus-rest-client-jackson`. {{% details title="Hint" %}} @@ -106,13 +128,13 @@ mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectArtifactId=quarkus-rest-data-consumer \ -DclassName="ch.puzzle.quarkustechlab.restconsumer.boundary.DataConsumerResource" \ -Dpath="/data" \ - -Dextensions="rest-rest-jackson" + -Dextensions="quarkus-rest-client-jackson" ``` {{% /details %}} -In the data-consumer microservice we will have another resource on the path `/data` which serves for now as a proxy to our `data-producer`. We will consume the data-producer microservices API with a service called `DataProducerService`. To achieve that, generate an interface called `DataProducerService` which mirrors the data-producer's DataResource. Annotate the `DataProducerService` with the MicroProfile annotation `@RegisterRestClient` to allow Quarkus to acces the interface for CDI Injection as a REST client. +In the `quarkus-rest-data-consumer` microservice we will have another resource on the path `/data` which serves for now as a proxy to our `quarkus-rest-data-producer`. We will consume the `quarkus-rest-data-producer` microservices API with a service called `DataProducerService`. To achieve that, generate an interface called `DataProducerService` which mirrors the `quarkus-rest-data-producer` DataResource. Annotate the `DataProducerService` with the MicroProfile annotation `@RegisterRestClient` to allow Quarkus to acces the interface for CDI Injection as a REST client. ```java package ch.puzzle.quarkustechlab.restconsumer.boundary; @@ -135,7 +157,7 @@ public interface DataProducerService { } ``` -Implement the same POJO `SensorMeasurement` as in the producer again for the `data-consumer` project but with an empty constructor. +Implement the same POJO `SensorMeasurement` as in the producer again for the `quarkus-rest-data-consumer` project but with an empty constructor. {{% details title="Hint" %}} @@ -177,7 +199,7 @@ quarkus.rest-client.data-producer-api.scope=jakarta.inject.Singleton ``` To use the registered RestClient in our application inject it into the `DataConsumerResource` and simply call the defined interface's method. To inject a RestClient into your desired class create a field of type `DataProducerService dataProducerService` and annotate it with `@RestClient`. -You can edit our resource in the `data-consumer` to use the `DataProducerService` to create a proxy consuming the API of the `data-producer` and return it. +You can edit our resource in the `quarkus-rest-data-consumer` to use the `DataProducerService` to create a proxy consuming the API of the `quarkus-rest-data-producer` and return it. ```java import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -204,10 +226,10 @@ public class DataConsumerResource { To run both microservices you have to alter the `application.properties` of the consumer and change its default port. Simply add `quarkus.http.port=8081` to your `application.properties` and the default port will be changed. -When you have both microservices running, try sending a request to the consumer. You will see that we receive a `SensorMeasurement`, which the `data-producer` produced. Probably you'll only see the generated object reference like this `SensorMeasurement@4c7758a8`. Do you remember what we did in the producer to get the json output? +When you have both microservices running, try sending a request to the consumer. You will see that we receive a `SensorMeasurement`, which the `quarkus-rest-data-producer` produced. Probably you'll only see the generated object reference like this `SensorMeasurement@4c7758a8`. Do you remember what we did in the producer to get the json output? {{% details title="Hint" %}} -To get the full json output you have to add the `rest-jackson` extension and make sure the media type is set to json. +To get the full json output you have to add the `quarkus-rest-jackson` extension and make sure the media type is set to json. {{% /details %}} diff --git a/content/en/docs/02.0/23_fault-tolerance.md b/content/en/docs/02.0/23_fault-tolerance.md index 4188d365..e13e7649 100644 --- a/content/en/docs/02.0/23_fault-tolerance.md +++ b/content/en/docs/02.0/23_fault-tolerance.md @@ -12,7 +12,7 @@ description: > As our application grows in complexity and in horizontal distribution, microservices will get their own lifecycles. We need our application to be more resilient when microservices are temporarily unavailable. In a complex structure where one HTTP request from an entrypoint triggers multiple other REST calls in our distributed system a single error at the end of the call-trace will result in chaotic behaviour if we don't anticipate this early enough. -The microprofile 'fault-tolerance' (Quarkus extension: `smallrye-fault-tolerance`) comes in very handy to implement simple but effective design patterns to be prepared for said events. Add this extension to both of your microservices! +The microprofile `fault-tolerance` (Quarkus extension: `quarkus-smallrye-fault-tolerance`) comes in very handy to implement simple but effective design patterns to be prepared for said events. Add this extension to both of your microservices! ```xml @@ -26,13 +26,15 @@ The microprofile 'fault-tolerance' (Quarkus extension: `smallrye-fault-tolerance ### {{% param sectionnumber %}}.1.1: Resilience through Retries -A common approach to gain resilience in our system is to add a retry mechanism. With the annotation `@Retry` we can enable an automated retries whenever the called method throws an RuntimeException. +A common approach to gain resilience in our system is to add a retry mechanism. With the annotation `@Retry` we can enable an automated retries whenever the called method throws a `RuntimeException`. Let's take a look at an example: -In the data-producer project let's change our REST endpoint, which serves data to the consumer, so it will fail randomly. +In the `quarkus-rest-data-producer` project let's change our REST endpoint, which serves data to the consumer, so it will fail randomly. ```java +package ch.puzzle.quarkustechlab.restproducer.boundary; + import java.util.Random; import jakarta.ws.rs.GET; @@ -67,9 +69,11 @@ As you can see we introduced a random boolean which will make the endpoint to fa If we start up both microservices and try to consume data multiple times, we can see that the rest-consumer has trouble to consume data when the producer throws an exception. The default behaviour is just to return the Exception when the endpoint is called. This behaviour is nothing we would like to have in our production ready environment. -In our data-consumer project we will add the retry mechanism. Add the extension `smallrye-fault-tolerance` if you don't already have and edit our DataProducerService interface: +In our `quarkus-rest-data-consumer` project we will add the retry mechanism. Add the extension `quarkus-smallrye-fault-tolerance` if you don't already have and edit our `DataProducerService` interface: ```java +package ch.puzzle.quarkustechlab.restconsumer.boundary; + import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; @@ -91,16 +95,18 @@ public interface DataProducerService { } ``` -You can see that we added the `@Retry` annotation and configured it to have a maximum of retries before it fails. Let's try to consume data again. If you send a request to your data-consumer you can see now from the amount of logs produced by the data-producer that after a failure the endpoint is instantaniously called again. +You can see that we added the `@Retry` annotation and configured it to have a maximum of retries before it fails. Let's try to consume data again. If you send a request to your `quarkus-rest-data-consumer` you can see now from the amount of logs produced by the `quarkus-rest-data-producer` that after a failure the endpoint is instantaniously called again. ### {{% param sectionnumber %}}.1.2: Timeouts -The `@Timeout` annotation can be used to mark functions which will have a finite amount of time before a TimeoutException will be thrown. +The `@Timeout` annotation can be used to mark functions which will have a finite amount of time before a `TimeoutException` will be thrown. -We update our producer to take a random amount of time to answer with the desired response. +We update our `quarkus-rest-data-producer` to take a random amount of time to answer with the desired response. ```java +package ch.puzzle.quarkustechlab.restproducer.boundary; + import java.util.Random; import jakarta.ws.rs.GET; @@ -129,9 +135,11 @@ public class DataResource { ``` -Then update the `DataProducerService` of our data-consumer to time out after `500ms`: +Then update the `DataProducerService` of our `quarkus-rest-data-consumer` to time out after `500ms`: ```java +package ch.puzzle.quarkustechlab.restconsumer.boundary; + import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; @@ -158,7 +166,7 @@ If you send a request to the consumer, you will see that about half of the time ### {{% param sectionnumber %}}.1.3: Fallbacks -When we insert timeouts or a maximum amount of retries for a certain part of our code, we want to handle these exceptional states. We can use the `@Fallback` annotation to define a fallback for the failing method. We can annotate a function with `@Fallback(fallbackMethod = "defaultMethod")` so when the annotated method fails, the defined fallback method `defaultMethod()` will be invoked instead. +When we insert timeouts or a maximum amount of retries for a certain part of our code, we want to handle these exceptional states. We can use the `@Fallback` annotation to define a fallback for the failing method. We can annotate a function with `@Fallback(fallbackMethod = "getDefaultMeasurement")` so when the annotated method fails, the defined fallback method `getDefaultMeasurement()` will be invoked instead. Let's update the example from before to use a fallback if it takes longer than the defined 500 ms to respond: diff --git a/content/en/docs/02.0/24_health.md b/content/en/docs/02.0/24_health.md index b3fd4766..3b789352 100644 --- a/content/en/docs/02.0/24_health.md +++ b/content/en/docs/02.0/24_health.md @@ -12,7 +12,7 @@ In this section we will add health checks to our microservices. ## Adding Health checks -We will be using the `smallrye-health` quarkus extension which relies on the MicroProfile Health specification. +We will be using the `quarkus-smallrye-health` quarkus extension which relies on the MicroProfile Health specification. This extension will expose the following REST endpoints: @@ -50,11 +50,11 @@ A simple health response may look like this ### Task {{% param sectionnumber %}}.1: Adding the dependency -Add the `smallrye-health` extension to your data-producer and data-consumer service. +Add the `quarkus-smallrye-health` extension to your `quarkus-rest-data-producer` and `quarkus-rest-data-consumer` service. {{% details title="Hint" %}} ```s -./mvnw quarkus:add-extension -Dextensions="smallrye-health" +./mvnw quarkus:add-extension -Dextensions="quarkus-smallrye-health" ``` If you are manually importing the health extension use the following dependency: @@ -89,8 +89,8 @@ curl localhost:8080/q/health ## Custom health check -As a simple example we will write a custom health check observing the last SensorMeasurement fetched from the -data-producer service. It should switch to failed state if the last fetched Measurement has some defined age. +As a simple example we will write a custom health check observing the last `SensorMeasurement` fetched from the +`quarkus-rest-data-producer` service. It should switch to failed state if the last fetched Measurement has some defined age. A health check is usually evaluated based on its http response code. The main target for health checks is providing a technical interface used by an underlying platform. Beside the http response code the health check can also provide some @@ -99,10 +99,15 @@ information in the response body. However, it is not evaluated to determine if t ### Task {{% param sectionnumber %}}.3: Write a custom liveness check -Create a `HealthService` which records the last message received from the data-producer. +Create a `HealthService` which records the last message received from the `quarkus-rest-data-producer`. {{% details title="HealthService Hint" %}} ```java +package ch.puzzle.quarkustechlab.restconsumer.control; + +import jakarta.enterprise.context.ApplicationScoped; +import java.time.Instant; + @ApplicationScoped public class HealthService { @@ -119,14 +124,26 @@ public class HealthService { ``` {{% /details %}} -Inject the `HealthService` to the `DataConsumerResource` and register the invocation to the data-producer. +Inject the `HealthService` to the `DataConsumerResource` and register the invocation to the remote `quarkus-rest-data-producer`. {{% details title="DataConsumerResource Hint" %}} ```java +package ch.puzzle.quarkustechlab.restconsumer.boundary; + +import ch.puzzle.quarkustechlab.restconsumer.control.HealthService; +import ch.puzzle.quarkustechlab.restconsumer.entity.SensorMeasurement; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RestClient; + @Path("/data") public class DataConsumerResource { - /* stripped for simplicity */ + @RestClient + DataProducerService dataProducerService; @Inject HealthService healthService; @@ -144,13 +161,24 @@ public class DataConsumerResource { Write a `RecentMessageHealthCheck` which implements `HealthCheck`. -* Fail the health check if the last fetched `SensorMeasurement` from the data-producer is older than 60 seconds. +* Fail the health check if the last fetched `SensorMeasurement` from the `quarkus-rest-data-producer` is older than 60 seconds. * Add the time the last message was fetched to the health check response -* Add the age of the last fetched SensorMeasurement to the health check response. +* Add the age of the last fetched `SensorMeasurement` to the health check response. You may start your `RecentMessageHealthCheck` with the following Template: ```java +package ch.puzzle.quarkustechlab.restconsumer.control; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Liveness; + +import java.time.Instant; + @Liveness @ApplicationScoped public class RecentMessageHealthCheck implements HealthCheck { @@ -170,6 +198,17 @@ public class RecentMessageHealthCheck implements HealthCheck { {{% details title="RecentMessageHealthCheck Hint" %}} ```java +package ch.puzzle.quarkustechlab.restconsumer.control; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Liveness; + +import java.time.Instant; + @Liveness @ApplicationScoped public class RecentMessageHealthCheck implements HealthCheck { @@ -198,7 +237,7 @@ public class RecentMessageHealthCheck implements HealthCheck { ## Health UI -The smallrye-health extension ships with a simple health ui. Point your browser to +The `quarkus-smallrye-health` extension ships with a simple health ui. Point your browser to and explore the health ui. ![Health UI](../health-ui.png) diff --git a/content/en/docs/02.0/25_reactive-programming.md b/content/en/docs/02.0/25_reactive-programming.md index 86e8ef05..ec95b004 100644 --- a/content/en/docs/02.0/25_reactive-programming.md +++ b/content/en/docs/02.0/25_reactive-programming.md @@ -23,6 +23,15 @@ If you are not familiar with reactive programming and are interested to go furth We will start similar to the previous example with a producer which exposes data on his REST endpoint. +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-reactive-rest-producer/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + ### Task {{% param sectionnumber %}}.1.1: Create producer project @@ -34,7 +43,7 @@ Start by creating a project `quarkus-reactive-rest-producer`. Add the extensions mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-reactive-rest-producer \ - -Dextensions="quarkus-resteasy-reactive-jackson" \ + -Dextensions="quarkus-rest-jackson" \ -DprojectVersion=1.0.0 \ -DclassName="ch.puzzle.quarkustechlab.reactiverest.producer.boundary.DataResource" ``` @@ -47,6 +56,10 @@ mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ The producer application should expose `SensorMeasurement` to the consumer. Let's create a simple class with the following content: ```java +package ch.puzzle.quarkustechlab.reactiverest.producer.entity; + +import java.time.Instant; + public class SensorMeasurement { public Long id; @@ -74,7 +87,6 @@ public class DataResource { public Uni getMeasurement() { return Uni.createFrom().item(new SensorMeasurement()); } - } ``` {{% /details %}} @@ -101,10 +113,10 @@ Response code: 200 (OK); Time: 115ms; Content length: 64 bytes To use the full potential of the reactive world we will need to have our database access reactive as well. Let's start by initializing a database. We don't need to worry about creating the database instance ourselves, the quarkus dev services will start our desired database in a container all by itself. -Add the extension `reactive-pg-client` to your project. +Add the extension `quarkus-reactive-pg-client` to your project. ```shell -./mvnw quarkus:add-extension -Dextensions="reactive-pg-client" +./mvnw quarkus:add-extension -Dextensions="quarkus-reactive-pg-client" ``` This will allow you to use a reactive way to connect and query your database. Configure the extension in your `application.properties` with the following content: @@ -143,13 +155,23 @@ Test if you can drop your table if it exists, re-create the table and fill it wi {{% details title="Hint" %}} ```java +package ch.puzzle.quarkustechlab.reactiverest.producer.boundary; + +import ch.puzzle.quarkustechlab.reactiverest.producer.entity.SensorMeasurement; +import io.smallrye.mutiny.Multi; +import io.vertx.mutiny.pgclient.PgPool; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @ApplicationScoped public class DBInit { private final PgPool client; private final boolean schemaCreate; - private static final Logger log = Logger.getLogger(DBInit.class.getName()); + private static final Logger log = LoggerFactory.getLogger(DBInit.class.getName()); public DBInit(PgPool client, @ConfigProperty(name = "myapp.schema.create", defaultValue = "true") boolean schemaCreate) { this.client = client; @@ -173,7 +195,6 @@ public class DBInit { .await().indefinitely(); } } - ``` {{% /details %}} @@ -182,7 +203,6 @@ Next up we will use an [Active Record](https://www.martinfowler.com/eaaCatalog/a Create a function `public static Multi findAll(PgPool client)` in your `SensorMeasurement` class. You can query and transform the result to a `Multi<...>` like this: ```java - client.query("SELECT id, data, time from sensormeasurements").execute() .onItem().transformToMulti(set -> Multi.createFrom().iterable(set)) .onItem().transform(SensorMeasurement::new) @@ -192,13 +212,11 @@ client.query("SELECT id, data, time from sensormeasurements").execute() The default constructor created can not handle the call for now. Let's update the constructor so we can create a `SensorMeasurement` from a `io.vertx.mutiny.sqlclient.Row`: ```java - public SensorMeasurement(Row row) { this.id = row.getLong("id"); this.data = row.getDouble("data"); this.time = Instant.from(row.getOffsetDateTime("time")); } - ``` In our `DataResource` we have to inject a `io.vertx.mutiny.pgclient.PgPool`. Alter your default `@GET` annotated function to return all `SensorMeasurement` objects in the database as a `Multi<...>`. @@ -206,6 +224,8 @@ In our `DataResource` we have to inject a `io.vertx.mutiny.pgclient.PgPool`. Alt {{% details title="Solution" %}} **DataResource.java:** ```java +package ch.puzzle.quarkustechlab.reactiverest.producer.boundary; + import io.smallrye.mutiny.Multi; import io.vertx.mutiny.pgclient.PgPool; @@ -231,6 +251,8 @@ public class DataResource { **DBInit.java:** ```java +package ch.puzzle.quarkustechlab.reactiverest.producer.control; + import io.quarkus.runtime.StartupEvent; import io.vertx.mutiny.pgclient.PgPool; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -244,7 +266,7 @@ public class DBInit { private final PgPool client; private final boolean schemaCreate; - private static final Logger log = Logger.getLogger(DBInit.class.getName()); + private static final Logger log = LoggerFactory.getLogger(DBInit.class.getName()); public DBInit(PgPool client, @ConfigProperty(name = "myapp.schema.create", defaultValue = "true") boolean schemaCreate) { this.client = client; @@ -272,6 +294,8 @@ public class DBInit { **SensorMeasurement.java:** ```java +package ch.puzzle.quarkustechlab.reactiverest.producer.entity; + import io.smallrye.mutiny.Multi; import io.vertx.mutiny.pgclient.PgPool; import io.vertx.mutiny.sqlclient.Row; @@ -319,7 +343,6 @@ Let's create two more API endpoints: To save a entity we will use the `client.preparedQuery` instead. You can use the prepared query like this: ```java - client.preparedQuery("INSERT INTO sensormeasurements (data, time) VALUES ($1, $2) RETURNING (id, data, time)") .execute(Tuple.of(data, time.atOffset(ZoneOffset.UTC))) @@ -382,13 +405,25 @@ curl -X POST localhost:8080/data -d "{\"data\":0.1, \"time\":\"2022-01-01T00:00: We have learned how to implement a reactive REST API to serve data in a complete reactive non-blocking way. Now it's time to take a look at the opposite, how do we reactively consume the API. -Create another Quarkus project with the following extensions: `quarkus-resteasy-reactive-jackson`, `quarkus-rest-client-reactive-jackson`: +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-reactive-rest-consumer/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + + +#### Implementation + +Create another Quarkus project `quarkus-reactive-rest-consumer` with the following extensions: `quarkus-rest-jackson`, `quarkus-rest-client-jackson`: ```bash mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-reactive-rest-consumer \ - -Dextensions="quarkus-resteasy-reactive-jackson,quarkus-rest-client-reactive-jackson" \ + -Dextensions="quarkus-rest-jackson,quarkus-rest-client-jackson" \ -DprojectVersion=1.0.0 \ -DclassName="ch.puzzle.quarkustechlab.reactiverest.consumer.boundary.DataResource" ``` @@ -402,21 +437,19 @@ quarkus.http.port=8081 We will duplicate the `SensorMeasurement` class from the producer but without the Active Record pattern functions. ```java +package ch.puzzle.quarkustechlab.reactiverest.consumer.entity; + import java.time.Instant; public class SensorMeasurement { - public Long id; public Double data; public Instant time; public SensorMeasurement() { - } - - public SensorMeasurement(Long id, Double data, Instant time) { this.id = id; - this.data = data; - this.time = time; + this.data = Math.random(); + this.time = Instant.now(); } } ``` @@ -424,13 +457,16 @@ public class SensorMeasurement { To consume the producer's REST API we create a `@RestClient` named `data-service`. Create a new interface `DataService` and annotate it with: ```java +package ch.puzzle.quarkustechlab.reactiverest.consumer.boundary; + +import jakarta.ws.rs.Path; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; @Path("/data") @RegisterRestClient(configKey = "data-service") public interface DataService { [...] } - ``` Define the producers API method headers and you have your service ready to go. @@ -439,11 +475,14 @@ Define the producers API method headers and you have your service ready to go. **DataService.java:** ```java -import io.smallrye.mutiny.Uni; -import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +package ch.puzzle.quarkustechlab.reactiverest.consumer.boundary; +import ch.puzzle.quarkustechlab.reactiverest.consumer.entity.SensorMeasurement; +import io.smallrye.mutiny.Uni; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + import java.util.List; @Path("/data") @@ -483,13 +522,16 @@ Let's create another REST API resource to tunnel the requests and consume the pr **DataResource.java:** ```java -import io.smallrye.mutiny.Uni; -import org.eclipse.microprofile.rest.client.inject.RestClient; +package ch.puzzle.quarkustechlab.reactiverest.consumer.boundary; +import ch.puzzle.quarkustechlab.reactiverest.consumer.entity.SensorMeasurement; +import io.smallrye.mutiny.Uni; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.rest.client.inject.RestClient; + @Path("/data") public class DataResource { @@ -540,7 +582,6 @@ Start by creating the endpoint for receiving the latest `SensorMeasurement`. Cre **...producer.boundary.DataResource:** ```java - [...] @GET @@ -553,12 +594,10 @@ Start by creating the endpoint for receiving the latest `SensorMeasurement`. Cre } [...] - ``` **...producer.entity.SensorMeasurement:** ```java - [...] public static Uni getLatest(PgPool client) { @@ -568,7 +607,6 @@ Start by creating the endpoint for receiving the latest `SensorMeasurement`. Cre } [...] - ``` {{% /details %}} @@ -579,7 +617,6 @@ Can you implement the similar API endpoint for calculating the average? {{% details title="Hint" %}} **...producer.boundary.DataResource:** ```java - [...] @GET @@ -592,12 +629,10 @@ Can you implement the similar API endpoint for calculating the average? } [...] - ``` **...producer.entity.SensorMeasurement:** ```java - [...] public static Uni getAverage(PgPool client) { @@ -605,6 +640,5 @@ Can you implement the similar API endpoint for calculating the average? .onItem().transform(RowSet::iterator) .onItem().transform(iterator -> iterator.hasNext() ? new SensorMeasurement(iterator.next()) : null); } - ``` {{% /details %}} diff --git a/content/en/docs/03.0/31_junit5.md b/content/en/docs/03.0/31_junit5.md index e255c236..5d85cd97 100644 --- a/content/en/docs/03.0/31_junit5.md +++ b/content/en/docs/03.0/31_junit5.md @@ -34,47 +34,39 @@ JUnit 5 changed some annotations in comparison to it's predecessor. One imoprtan JUnit 4: ```java - @Test(expected = Exception.class) public void shouldRaiseAnException() throws Exception { // ... } - ``` Instead we use the `assertThrows` method: ```java - public void shouldRaiseAnException() throws Exception { Assertions.assertThrows(Exception.class, () -> { //... }); } - ``` The same goes for the `timeout` attribute in JUnit 4: ```java - @Test(timeout = 1) public void shouldFailBecauseTimeout() throws InterruptedException { Thread.sleep(10); } - ``` Now, the assertTimeout method in JUnit 5: ```java - @Test public void shouldFailBecauseTimeout() throws InterruptedException { Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10)); } - ``` Some other annotations were renamed for readability reasons: @@ -91,20 +83,17 @@ Some other annotations were renamed for readability reasons: In JUnit 5 assertions can now be written in a lambda expression: ```java - @Test public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() { Assertions.assertTrue( 2 == 3, () -> "Numbers " + 2 + " and " + 3 + " are not equal!"); } - ``` Additionally we can group assertions in JUnit 5: ```java - @Test public void shouldAssertAllTheGroup() { List list = Arrays.asList(1, 2, 4); @@ -113,7 +102,6 @@ public void shouldAssertAllTheGroup() { () -> Assertions.assertEquals(list.get(1).intValue(), 2), () -> Assertions.assertEquals(list.get(2).intValue(), 3)); } - ``` @@ -122,7 +110,6 @@ public void shouldAssertAllTheGroup() { With JUnit 5, you can add the `@DisplayName` annotation to classes and methods. The name is used when generating reports, which makes it easier to describe the purpose of tests and track down failures, for example: ```java - @DisplayName("Test MyClass") class MyClassTest { @Test @@ -131,5 +118,4 @@ class MyClassTest { // ... } } - ``` diff --git a/content/en/docs/03.0/32_testing.md b/content/en/docs/03.0/32_testing.md index 3ad8697c..041940af 100644 --- a/content/en/docs/03.0/32_testing.md +++ b/content/en/docs/03.0/32_testing.md @@ -45,14 +45,12 @@ public class GreetingResourceTest { .when().get("/hello") .then() .statusCode(200) - .body(is("Hello from RESTEasy Reactive")); + .body(is("Hello from Quarkus REST")); } - - } ``` -If you are familiar with the JUnit 5 framework there are no big surprises here. The annotation `@QuarkusTest` instructs JUnit to start the application before the tests. The above shown test simply tests if the GET request to the `/hello` endpoint returns a response with HTTP status code 200 and body `"Hello from RESTEasy Reactive"` - nothing fancy. +If you are familiar with the JUnit 5 framework there are no big surprises here. The annotation `@QuarkusTest` instructs JUnit to start the application before the tests. The above shown test simply tests if the GET request to the `/hello` endpoint returns a response with HTTP status code 200 and body `"Hello from Quarkus REST"` - nothing fancy. You can run your tests using Maven: diff --git a/content/en/docs/03.0/33_handson.md b/content/en/docs/03.0/33_handson.md index 199ea7ff..1ea1ca12 100644 --- a/content/en/docs/03.0/33_handson.md +++ b/content/en/docs/03.0/33_handson.md @@ -12,13 +12,28 @@ In this section we are going to extend our created REST microservices and create ## Task {{% param sectionnumber %}}.1: Testing your Quarkus producer -Start can start with your `quarkus-rest-data-producer` project. Add tests for verifying your producing interfaces. Try to use the different techniques for mocking your injected beans. +You can start with your `quarkus-rest-data-producer` project. Add tests for verifying your producing interfaces. Try to use the different techniques for mocking your injected beans. -For demonstration purposes we implement a new endpoint `/dummy` which simply returns a String `dummy` with the help of an injected `DummyService`. +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-rest-data-producer/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + +#### Implementation + +For demonstration purposes we implement a new endpoint `/data/dummy` which simply returns a String `dummy` with the help of an injected `DummyService`. It could look something like this: ```java +package ch.puzzle.quarkustechlab.restproducer.control; + +import jakarta.enterprise.context.ApplicationScoped; + @ApplicationScoped public class DummyService { diff --git a/content/en/docs/04.0/42_jvm-build.md b/content/en/docs/04.0/42_jvm-build.md index 4d3a8a36..cbd57b8b 100644 --- a/content/en/docs/04.0/42_jvm-build.md +++ b/content/en/docs/04.0/42_jvm-build.md @@ -31,6 +31,7 @@ To build a Quarkus application to be run with the JVM you can use the provided D ~/quarkus-rest-data-consumer$ ./mvnw clean package ~$ docker build -f quarkus-rest-data-producer/src/main/docker/Dockerfile.jvm -t quarkus-rest-data-producer:latest quarkus-rest-data-producer/. + ~$ docker build -f quarkus-rest-data-consumer/src/main/docker/Dockerfile.jvm -t quarkus-rest-data-consumer:latest quarkus-rest-data-consumer/. ``` diff --git a/content/en/docs/08.0/82_kafka.md b/content/en/docs/08.0/82_kafka.md index 0b52b75b..f3a3b5fb 100644 --- a/content/en/docs/08.0/82_kafka.md +++ b/content/en/docs/08.0/82_kafka.md @@ -21,6 +21,20 @@ Apache Kafka is an event streaming platform used to collect, process, store, and ### {{% param sectionnumber %}}.2: Local Development + +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`. +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + +##### Producer +{{< csvtable csv="/solution/quarkus-reactive-messaging-producer/dependencies.csv" class="dependencies" >}} + +##### Consumer +{{< csvtable csv="/solution/quarkus-reactive-messaging-consumer/dependencies.csv" class="dependencies" >}} + +#### Implementation + For local development we do have the choice to either run our Kafka services via Quarkus Devservices or with docker-compose. If you want to use the Quarkus Devservices simply remove the line `kafka.bootstrap.servers=localhost:9092` in your `applications.properties` file. This will set up a [Redpanda](https://vectorized.io/redpanda) container for your development environment. @@ -29,38 +43,19 @@ If you choose to test your local services with a Kafka broker you can use a smal to start a Kafka cluster: ```yaml - -version: '3' +version: '2' services: - - zookeeper: - image: {{% param "strimziVersion" %}} - command: [ - "sh", "-c", - "bin/zookeeper-server-start.sh config/zookeeper.properties" - ] - ports: - - "2181:2181" - environment: - LOG_DIR: /tmp/logs - kafka: image: {{% param "strimziVersion" %}} command: [ - "sh", "-c", - "bin/kafka-server-start.sh config/server.properties --override listeners=$${KAFKA_LISTENERS} --override advertised.listeners=$${KAFKA_ADVERTISED_LISTENERS} --override zookeeper.connect=$${KAFKA_ZOOKEEPER_CONNECT}" + "sh", "-c", + "./bin/kafka-storage.sh format -t $$(./bin/kafka-storage.sh random-uuid) -c ./config/kraft/server.properties && ./bin/kafka-server-start.sh ./config/kraft/server.properties" ] - depends_on: - - zookeeper ports: - - "9092:9092" + - "9092:9092" environment: LOG_DIR: "/tmp/logs" - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 - KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - ``` Start your cluster with: @@ -70,21 +65,22 @@ Start your cluster with: ``` {{% /details %}} -Create again two Quarkus projects `quarkus-reactive-messaging-consumer` and `quarkus-reactive-messaging-producer`. +Create two Quarkus projects `quarkus-reactive-messaging-consumer` and `quarkus-reactive-messaging-producer`. ```s # Create producer application mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-reactive-messaging-producer \ - -Dextensions="smallrye-reactive-messaging-kafka,quarkus-jackson,quarkus-jsonb" \ + -Dextensions="quarkus-messaging-kafka,quarkus-jackson,quarkus-jsonb" \ -DprojectVersion=1.0.0 # Create consumer application mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-reactive-messaging-consumer \ - -Dextensions="smallrye-reactive-messaging-kafka,quarkus-resteasy-reactive-jackson,quarkus-resteasy-reactive-jsonb" \ + -Dextensions="quarkus-messaging-kafka,quarkus-rest-jackson,quarkus-rest-jsonb" \ + -DclassName="ch.puzzle.quarkustechlab.messaging.consumer.boundary.DataResource" -DprojectVersion=1.0.0 ``` @@ -99,7 +95,7 @@ As we do not include resteasy in your producer the producer does not have a ui a {{% /alert %}} -Next, create the SensorMeasurement in both project. +Next, create the SensorMeasurement in both project in the package `ch.puzzle.quarkustechlab.messaging.[producer|consumer].entity`. ```java public class SensorMeasurement { @@ -153,8 +149,8 @@ public class ReactiveDataProducer { To ensure the connection from the connector to your message broker we need some configuration in our `application.properties`. ```s -# If you'd like to use Redpanda from the devservices instead of a docker-compose kafka cluster simply comment or remove the line below -kafka.bootstrap.servers=localhost:9092 +# Uncomment if you do not want to use the devservices redpanda container. +# kafka.bootstrap.servers=localhost:9092 mp.messaging.outgoing.data.connector=smallrye-kafka mp.messaging.outgoing.data.topic=data @@ -224,8 +220,8 @@ After creating the deserializer we need to set up the connectors for the consume ```s quarkus.http.port=8081 -# If you'd like to use Redpanda from the devservices instead of a docker-compose kafka cluster simply comment or remove the line below -kafka.bootstrap.servers=localhost:9092 +# Uncomment if you do not want to use the devservices redpanda container. +# kafka.bootstrap.servers=localhost:9092 mp.messaging.incoming.data.connector=smallrye-kafka mp.messaging.incoming.data.topic=data diff --git a/content/en/docs/08.0/83_cloudevents.md b/content/en/docs/08.0/83_cloudevents.md index 2ab373b5..8a10e54c 100644 --- a/content/en/docs/08.0/83_cloudevents.md +++ b/content/en/docs/08.0/83_cloudevents.md @@ -58,6 +58,17 @@ time | Timestamp [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) | Tim In addition to the specification of CloudEvents itself, there are extensions giving extra flexibility for other meta fields to enrich your event. For example OpenTracing header fields can be added by the [Distributed Tracing](https://github.com/cloudevents/spec/blob/main/cloudevents/extensions/distributed-tracing.md) extension. +### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`. +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + +#### Producer +{{< csvtable csv="/solution/quarkus-cloudevents-producer/dependencies.csv" class="dependencies" >}} + +#### Consumer +{{< csvtable csv="/solution/quarkus-cloudevents-consumer/dependencies.csv" class="dependencies" >}} + ### {{% param sectionnumber %}}.2: Implementation @@ -74,13 +85,13 @@ We create two new Quarkus projects: mvn io.quarkus.platform:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-cloudevents-producer \ - -Dextensions="resteasy-reactive,smallrye-reactive-messaging-kafka" + -Dextensions="quarkus-rest,quarkus-messaging-kafka" # Create consumer application mvn io.quarkus.platform:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-cloudevents-consumer \ - -Dextensions="resteasy-reactive,smallrye-reactive-messaging-kafka" + -Dextensions="quarkus-rest,quarkus-messaging-kafka" ``` Remove the test classes and add the following extensions to your projects' `pom.xml`: @@ -202,8 +213,8 @@ public class MeasurementsResource { Of course, we need some configuration in the `application.properties` to emit the events to our Kafka broker. ```properties -# If you'd like to use Redpanda from the devservices instead of a docker-compose kafka cluster simply comment or remove the line below -kafka.bootstrap.servers=localhost:9092 +# Uncomment if you do not want to use the devservices redpanda container. +# kafka.bootstrap.servers=localhost:9092 mp.messaging.outgoing.measurements.connector=smallrye-kafka mp.messaging.outgoing.measurements.value.serializer=io.confluent.kafka.serializers.KafkaAvroSerializer @@ -302,6 +313,7 @@ public class EventListener { return message.ack(); } +} ``` Add the following properties to the `application.properties` file: @@ -309,8 +321,8 @@ Add the following properties to the `application.properties` file: ```properties quarkus.http.port=8081 -# If you'd like to use Redpanda from the devservices instead of a docker-compose kafka cluster simply comment or remove the line below -kafka.bootstrap.servers=localhost:9092 +# Uncomment if you do not want to use the devservices redpanda container. +# kafka.bootstrap.servers=localhost:9092 mp.messaging.incoming.measurements.connector=smallrye-kafka mp.messaging.incoming.measurements.topic=measurements diff --git a/content/en/docs/09.0/92_jaeger.md b/content/en/docs/09.0/92_jaeger.md index 203fb291..809c40f2 100644 --- a/content/en/docs/09.0/92_jaeger.md +++ b/content/en/docs/09.0/92_jaeger.md @@ -12,13 +12,25 @@ We are going to test the OpenTelemetry API live with Jaeger as our tracing servi ## Task {{% param sectionnumber %}}.1: Create the new service + +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-opentelemetry-jaeger/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + +### Implementation + Create a new Quarkus application, in this example we are going to use a simple reactive rest application: ```bash mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -DprojectGroupId=ch.puzzle \ -DprojectArtifactId=quarkus-opentelemetry-jaeger \ - -Dextensions="resteasy-reactive,quarkus-opentelemetry" \ + -Dextensions="quarkus-rest,quarkus-opentelemetry" \ -DprojectVersion=1.0.0 \ -DclassName="ch.puzzle.quarkustechlab.opentelemetry.jaeger.boundary.TracedResource" ``` @@ -58,7 +70,7 @@ public class TracedResource { @Produces(MediaType.TEXT_PLAIN) @WithSpan public String hello() { - return "Hello from RESTEasy Reactive"; + return "Hello from Quarkus REST"; } } ``` diff --git a/content/en/docs/09.0/93_metrics.md b/content/en/docs/09.0/93_metrics.md index 4f684577..ee635d6a 100644 --- a/content/en/docs/09.0/93_metrics.md +++ b/content/en/docs/09.0/93_metrics.md @@ -11,7 +11,24 @@ description: > Knowing about the state of your applications in a microservice architecture is crucial. We will learn in this chapter how to use the metrics extension to get insights about our applications. -You can start by copying the rest application `data-producer` and `data-consumer` or you can just use these project and enhance them with metrics. Add the `micrometer-registry-prometheus` to both of your projects. This will add the micrometer as well as the according prometheus extension to your project. +You can start by copying the rest application `quarkus-rest-data-producer` and `quarkus-rest-data-consumer` or you can just use these project and enhance them with metrics. + + +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`. +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + +##### Producer +{{< csvtable csv="/solution/quarkus-metrics-data-producer/dependencies.csv" class="dependencies" >}} + +##### Consumer +{{< csvtable csv="/solution/quarkus-metrics-data-consumer/dependencies.csv" class="dependencies" >}} + + +### Implementation + +Add the `quarkus-micrometer-registry-prometheus` extension to both of your projects. This will add the micrometer as well as the according prometheus extension to your project. {{% details title="Hint" %}} @@ -146,10 +163,28 @@ As you can see we already have a timed function which exposes it's metric, but w {{% details title="Hint" %}} ```java +package ch.puzzle.quarkustechlab.restconsumer.boundary; + +import ch.puzzle.quarkustechlab.restconsumer.control.HealthService; +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import ch.puzzle.quarkustechlab.restconsumer.entity.SensorMeasurement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.Supplier; + @Path("/data") public class DataConsumerResource { - private final Logger logger = Logger.getLogger(DataConsumerResource.class.getName()); + private final Logger logger = LoggerFactory.getLogger(DataConsumerResource.class); private final MeterRegistry registry; @RestClient diff --git a/content/en/docs/10.0/102_business-code.md b/content/en/docs/10.0/102_business-code.md index baaf1058..121ead00 100644 --- a/content/en/docs/10.0/102_business-code.md +++ b/content/en/docs/10.0/102_business-code.md @@ -109,6 +109,8 @@ the information on the servlet. However, for simplicity of the lab it is suffici Create the following `AppinfoNames.java` class: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + public class AppinfoNames { public static final String EXTENSION_NAME = "appinfo"; public static final String CONFIG_PREFIX = "quarkus."+ EXTENSION_NAME; @@ -119,6 +121,8 @@ public class AppinfoNames { Your `Appinfo.java` class should look something like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + public class Appinfo { String buildTime; @@ -157,6 +161,8 @@ public class Appinfo { Your `BuildInfo.java` class should look something like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + public class BuildInfo { String time; @@ -248,6 +254,21 @@ Arc.container().instance(BuildInfo.class).get(); Completing the TODOs in the `AppinfoService.java` the class should look like below. ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + +import jakarta.inject.Singleton; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.CDI; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Instant; +import java.util.Map; + @Singleton public class AppinfoService { @@ -332,6 +353,16 @@ public class AppinfoServlet extends HttpServlet { {{% details title="Task hint" %}} Your class should look like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + +import jakarta.enterprise.inject.spi.CDI; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + @WebServlet public class AppinfoServlet extends HttpServlet { diff --git a/content/en/docs/10.0/103_configuration.md b/content/en/docs/10.0/103_configuration.md index 5b3ce4c6..5201b9cc 100644 --- a/content/en/docs/10.0/103_configuration.md +++ b/content/en/docs/10.0/103_configuration.md @@ -67,6 +67,13 @@ public class AppinfoBuildTimeConfig { The build-time configuration looks like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.deployment; + +import ch.puzzle.quarkustechlab.extensions.appinfo.runtime.AppinfoNames; +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + @ConfigRoot(name = AppinfoNames.EXTENSION_NAME, phase = ConfigPhase.BUILD_TIME) public class AppinfoBuildTimeConfig { @@ -117,6 +124,12 @@ public class AppinfoRunTimeConfig { The run-time configuration looks like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + +import io.quarkus.runtime.annotations.ConfigItem; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; + @ConfigRoot(name = AppinfoNames.EXTENSION_NAME, phase = ConfigPhase.RUN_TIME) public class AppinfoRunTimeConfig { diff --git a/content/en/docs/10.0/104_processor-build-steps.md b/content/en/docs/10.0/104_processor-build-steps.md index 7e604059..5e2a51fb 100644 --- a/content/en/docs/10.0/104_processor-build-steps.md +++ b/content/en/docs/10.0/104_processor-build-steps.md @@ -95,6 +95,11 @@ public class AppinfoRecorder { The recorder looks like this: ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.runtime; + +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.annotations.Recorder; + @Recorder public class AppinfoRecorder { @@ -155,30 +160,58 @@ private static boolean shouldInclude(LaunchModeBuildItem launchMode, AppinfoBuil Your code may look like this: ```java -private static final Logger logger = LoggerFactory.getLogger(TechlabExtensionAppinfoProcessor.class); +package ch.puzzle.quarkustechlab.extensions.appinfo.deployment; -@BuildStep -@Record(STATIC_INIT) -void syntheticBean(AppinfoBuildTimeConfig appinfoConfig, - LaunchModeBuildItem launchMode, - AppinfoRecorder recorder, - BuildProducer syntheticBeans) { +import ch.puzzle.quarkustechlab.extensions.appinfo.runtime.AppinfoRecorder; +import ch.puzzle.quarkustechlab.extensions.appinfo.runtime.BuildInfo; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import jakarta.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - if(shouldInclude(launchMode, appinfoConfig)) { - String buildTime = appinfoConfig.recordBuildTime ? Instant.now().toString() : null; - String builtFor = appinfoConfig.builtFor; +import java.time.Instant; - logger.info("Adding BuildInfo. RecordBuildTime={}, BuiltFor={}", appinfoConfig.recordBuildTime, builtFor); +import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; - syntheticBeans.produce(SyntheticBeanBuildItem.configure(BuildInfo.class).scope(Singleton.class) - .runtimeValue(recorder.createBuildInfo(buildTime, builtFor)) - .unremovable() - .done()); +class TechlabExtensionAppinfoProcessor { + + private static final Logger logger = LoggerFactory.getLogger(TechlabExtensionAppinfoProcessor.class); + + private static final String FEATURE = "techlab-extension-appinfo"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); } -} -private static boolean shouldInclude(LaunchModeBuildItem launchMode, AppinfoBuildTimeConfig appinfoConfig) { - return launchMode.getLaunchMode().isDevOrTest() || appinfoConfig.alwaysInclude; + @BuildStep + @Record(STATIC_INIT) + void syntheticBean(AppinfoBuildTimeConfig appinfoConfig, + LaunchModeBuildItem launchMode, + AppinfoRecorder recorder, + BuildProducer syntheticBeans) { + + if(shouldInclude(launchMode, appinfoConfig)) { + String buildTime = appinfoConfig.recordBuildTime ? Instant.now().toString() : null; + String builtFor = appinfoConfig.builtFor; + + logger.info("Adding BuildInfo. RecordBuildTime={}, BuiltFor={}", appinfoConfig.recordBuildTime, builtFor); + + syntheticBeans.produce(SyntheticBeanBuildItem.configure(BuildInfo.class).scope(Singleton.class) + .runtimeValue(recorder.createBuildInfo(buildTime, builtFor)) + .unremovable() + .done()); + } + } + + private static boolean shouldInclude(LaunchModeBuildItem launchMode, AppinfoBuildTimeConfig appinfoConfig) { + return launchMode.getLaunchMode().isDevOrTest() || appinfoConfig.alwaysInclude; + } } ``` {{% /details %}} diff --git a/content/en/docs/10.0/105_build-and-use.md b/content/en/docs/10.0/105_build-and-use.md index c4577b95..89b20367 100644 --- a/content/en/docs/10.0/105_build-and-use.md +++ b/content/en/docs/10.0/105_build-and-use.md @@ -34,7 +34,19 @@ mvn io.quarkus:quarkus-maven-plugin:{{% param "quarkusVersion" %}}:create \ -Dpath="/demo" ``` -This simple Quarkus application needs to have the following dependency: + +#### Maven dependencies reference + +The solution for this lab uses the following dependencies in the `pom.xml`: + +{{< csvtable csv="/solution/quarkus-appinfo-application/dependencies.csv" class="dependencies" >}} + +Be aware that `quarkus.platform.version` and `quarkus-plugin.version` should be set to `{{% param "quarkusVersion" %}}` in your `pom.xml`. + + +### Implementation + +If you started with your own Application make sure you add the following dependency: ```xml ch.puzzle diff --git a/content/en/docs/10.0/106_dev-console.md b/content/en/docs/10.0/106_dev-console.md index 0dc8d130..b4dac7d5 100644 --- a/content/en/docs/10.0/106_dev-console.md +++ b/content/en/docs/10.0/106_dev-console.md @@ -277,6 +277,17 @@ Finally, we have to configure our dev ui card to create and link to our configur {{% details title="Complete AppinfoDevUiProcessor" %}} ```java +package ch.puzzle.quarkustechlab.extensions.appinfo.deployment.devui; + +import ch.puzzle.quarkustechlab.extensions.appinfo.deployment.StaticMetadataBuildItem; +import ch.puzzle.quarkustechlab.extensions.appinfo.runtime.ConfigSourceJsonRPCService; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.devui.spi.JsonRPCProvidersBuildItem; +import io.quarkus.devui.spi.page.CardPageBuildItem; +import io.quarkus.devui.spi.page.Page; +import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; + public class AppinfoDevUiProcessor { @BuildStep(onlyIf = IsDevelopment.class) diff --git a/dependencies.xsl b/dependencies.xsl new file mode 100644 index 00000000..b0969c43 --- /dev/null +++ b/dependencies.xsl @@ -0,0 +1,62 @@ + + + + + + + + + groupId + artifactId + version + scope + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/go.sum b/go.sum index 58a5e87e..466800ea 100644 --- a/go.sum +++ b/go.sum @@ -9,5 +9,6 @@ github.com/google/docsy/dependencies v0.4.0 h1:FXwyjtuFfPIPBauU2t7uIAgS6VYfJf+OD github.com/google/docsy/dependencies v0.4.0/go.mod h1:2zZxHF+2qvkyXhLZtsbnqMotxMukJXLaf8fAZER48oo= github.com/puzzle/docsy-puzzle v0.0.0-20220406081603-2cd9f7c8d79a h1:ivuXhwliGTmfp4Zn9dqHiIHPUbniLhsbSYKrsQIoFKM= github.com/puzzle/docsy-puzzle v0.0.0-20220406081603-2cd9f7c8d79a/go.mod h1:FHtQEgHYfsiO5d1XXaF/mD5C51PQw1kea8JwTGBs93o= +github.com/puzzle/docsy-puzzle v0.0.0-20230123144731-757054047a02 h1:80gTlzoKpnRjr4F70KAXmNs6UsTAkPgYEyyVguDwheg= github.com/puzzle/docsy-puzzle v0.0.0-20230123144731-757054047a02/go.mod h1:q4bPnnpLaz5IDdFmQFxCHr85uwAsK9ayut5NNmC4w3I= github.com/twbs/bootstrap v4.6.1+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= diff --git a/layouts/partials/hooks/head-end.html b/layouts/partials/hooks/head-end.html new file mode 100644 index 00000000..8c2e689a --- /dev/null +++ b/layouts/partials/hooks/head-end.html @@ -0,0 +1,4 @@ +{{ $dep := resources.Get "css/dep.css" }} + + + \ No newline at end of file diff --git a/layouts/shortcodes/csvtable.html b/layouts/shortcodes/csvtable.html new file mode 100644 index 00000000..656c9b34 --- /dev/null +++ b/layouts/shortcodes/csvtable.html @@ -0,0 +1,29 @@ + + +{{- $rows := "" -}} +{{- $del := "," -}} +{{- $class := .Get "class" -}} +{{- $options := dict "delimiter" $del -}} +{{- $csv := .Get "csv" | readFile -}} +{{- $rows := $csv | transform.Unmarshal $options -}} +{{- $headerRow := index $rows 0 -}} +{{- $dataRows := after 1 $rows -}} + + + + {{- range $headerRow -}} + + {{- end -}} + + + + {{- range $rowIndex, $columns := $dataRows -}} + + {{- range $index, $col := $columns -}} + + {{- end -}} + + {{- end -}} + +
{{ . }}
{{ . }}
+ diff --git a/solution/dev-services/pom.xml b/solution/dev-services/pom.xml index 3b33db6b..ff96a0e2 100644 --- a/solution/dev-services/pom.xml +++ b/solution/dev-services/pom.xml @@ -6,17 +6,17 @@ dev-services 1.0.0-SNAPSHOT - 3.8.1 + 3.12.1 true - 11 - 11 + 17 + 17 UTF-8 UTF-8 - 2.13.2.Final + 3.11.1 quarkus-universe-bom io.quarkus - 2.13.2.Final - 3.0.0-M5 + 3.11.1 + 3.2.3 @@ -42,6 +42,10 @@ io.quarkus quarkus-resteasy-jsonb
+ + org.flywaydb + flyway-database-postgresql + io.quarkus quarkus-jdbc-postgresql @@ -133,7 +137,8 @@ - native + true + false diff --git a/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/boundary/EmployeeResource.java b/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/boundary/EmployeeResource.java index c6181dd9..a492a222 100644 --- a/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/boundary/EmployeeResource.java +++ b/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/boundary/EmployeeResource.java @@ -2,10 +2,10 @@ import ch.puzzle.quarkustechlab.entity.Employee; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import java.util.List; @Path("/employee") diff --git a/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/entity/Employee.java b/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/entity/Employee.java index 0b558d07..71eb847a 100644 --- a/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/entity/Employee.java +++ b/solution/dev-services/src/main/java/ch/puzzle/quarkustechlab/entity/Employee.java @@ -2,10 +2,10 @@ import io.quarkus.hibernate.orm.panache.PanacheEntity; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; @Entity @Table(name = "employee") diff --git a/solution/kafka-stack/docker/docker-compose.yml b/solution/kafka-stack/docker/docker-compose.yml index f52352ce..cf44efe8 100644 --- a/solution/kafka-stack/docker/docker-compose.yml +++ b/solution/kafka-stack/docker/docker-compose.yml @@ -1,31 +1,13 @@ version: '2' services: - - zookeeper: - image: quay.io/strimzi/kafka:0.37.0-kafka-3.5.1 - command: [ - "sh", "-c", - "bin/zookeeper-server-start.sh config/zookeeper.properties" - ] - ports: - - "2181:2181" - environment: - LOG_DIR: /tmp/logs - kafka: - image: quay.io/strimzi/kafka:0.37.0-kafka-3.5.1 + image: quay.io/strimzi/kafka:0.41.0-kafka-3.7.0 command: [ - "sh", "-c", - "bin/kafka-server-start.sh config/server.properties --override listeners=$${KAFKA_LISTENERS} --override advertised.listeners=$${KAFKA_ADVERTISED_LISTENERS} --override zookeeper.connect=$${KAFKA_ZOOKEEPER_CONNECT}" + "sh", "-c", + "./bin/kafka-storage.sh format -t $$(./bin/kafka-storage.sh random-uuid) -c ./config/kraft/server.properties && ./bin/kafka-server-start.sh ./config/kraft/server.properties" ] - depends_on: - - zookeeper ports: - "9092:9092" environment: - LOG_DIR: "/tmp/logs" - BROKER_ID: "0" - KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 - KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + LOG_DIR: "/tmp/logs" \ No newline at end of file diff --git a/solution/quarkus-appinfo-application/.dockerignore b/solution/quarkus-appinfo-application/.dockerignore new file mode 100644 index 00000000..94810d00 --- /dev/null +++ b/solution/quarkus-appinfo-application/.dockerignore @@ -0,0 +1,5 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* \ No newline at end of file diff --git a/solution/quarkus-appinfo-application/.gitignore b/solution/quarkus-appinfo-application/.gitignore new file mode 100644 index 00000000..8c7863e7 --- /dev/null +++ b/solution/quarkus-appinfo-application/.gitignore @@ -0,0 +1,43 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ diff --git a/solution/quarkus-appinfo-application/.mvn/wrapper/.gitignore b/solution/quarkus-appinfo-application/.mvn/wrapper/.gitignore new file mode 100644 index 00000000..e72f5e8b --- /dev/null +++ b/solution/quarkus-appinfo-application/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/solution/quarkus-appinfo-application/.mvn/wrapper/MavenWrapperDownloader.java b/solution/quarkus-appinfo-application/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..84d1e60d --- /dev/null +++ b/solution/quarkus-appinfo-application/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public final class MavenWrapperDownloader +{ + private static final String WRAPPER_VERSION = "3.2.0"; + + private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); + + public static void main( String[] args ) + { + log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); + + if ( args.length != 2 ) + { + System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); + System.exit( 1 ); + } + + try + { + log( " - Downloader started" ); + final URL wrapperUrl = new URL( args[0] ); + final String jarPath = args[1].replace( "..", "" ); // Sanitize path + final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); + downloadFileFromURL( wrapperUrl, wrapperJarPath ); + log( "Done" ); + } + catch ( IOException e ) + { + System.err.println( "- Error downloading: " + e.getMessage() ); + if ( VERBOSE ) + { + e.printStackTrace(); + } + System.exit( 1 ); + } + } + + private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) + throws IOException + { + log( " - Downloading to: " + wrapperJarPath ); + if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) + { + final String username = System.getenv( "MVNW_USERNAME" ); + final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); + Authenticator.setDefault( new Authenticator() + { + @Override + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication( username, password ); + } + } ); + } + try ( InputStream inStream = wrapperUrl.openStream() ) + { + Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); + } + log( " - Downloader complete" ); + } + + private static void log( String msg ) + { + if ( VERBOSE ) + { + System.out.println( msg ); + } + } + +} diff --git a/solution/quarkus-appinfo-application/.mvn/wrapper/maven-wrapper.properties b/solution/quarkus-appinfo-application/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..346d645f --- /dev/null +++ b/solution/quarkus-appinfo-application/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/solution/quarkus-appinfo-application/README.md b/solution/quarkus-appinfo-application/README.md new file mode 100644 index 00000000..5a9f7cc6 --- /dev/null +++ b/solution/quarkus-appinfo-application/README.md @@ -0,0 +1,56 @@ +# quarkus-appinfo-application + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +```shell script +./mvnw compile quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. + +## Packaging and running the application + +The application can be packaged using: +```shell script +./mvnw package +``` +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: +```shell script +./mvnw package -Dquarkus.package.jar.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/quarkus-appinfo-application-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling. + +## Provided Code + +### REST + +Easily start your REST Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) diff --git a/solution/quarkus-appinfo-application/mvnw b/solution/quarkus-appinfo-application/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/solution/quarkus-appinfo-application/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/solution/quarkus-appinfo-application/mvnw.cmd b/solution/quarkus-appinfo-application/mvnw.cmd new file mode 100644 index 00000000..c4586b56 --- /dev/null +++ b/solution/quarkus-appinfo-application/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/solution/quarkus-appinfo-application/pom.xml b/solution/quarkus-appinfo-application/pom.xml new file mode 100644 index 00000000..b7e99c08 --- /dev/null +++ b/solution/quarkus-appinfo-application/pom.xml @@ -0,0 +1,130 @@ + + + 4.0.0 + ch.puzzle + quarkus-appinfo-application + 1.0.0-SNAPSHOT + + + 3.12.1 + 21 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.11.1 + true + 3.2.5 + + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-rest + + + ch.puzzle + techlab-extension-appinfo + 1.0.0-SNAPSHOT + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + native + + + native + + + + false + true + + + + diff --git a/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.jvm b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.jvm new file mode 100644 index 00000000..1e265339 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.jvm @@ -0,0 +1,97 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-appinfo-application-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-21:1.19 + +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" ] + diff --git a/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.legacy-jar b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 00000000..18181234 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,93 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/quarkus-appinfo-application-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-21:1.19 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +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" ] diff --git a/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native new file mode 100644 index 00000000..a7d73d93 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-appinfo-application . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native-micro b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native-micro new file mode 100644 index 00000000..09d6b555 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,30 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/quarkus-appinfo-application . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-appinfo-application +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/solution/quarkus-appinfo-application/src/main/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResource.java b/solution/quarkus-appinfo-application/src/main/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResource.java new file mode 100644 index 00000000..b6b2cb27 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResource.java @@ -0,0 +1,16 @@ +package ch.puzzle.quarkustechlab.extensions.appinfo.application.boundary; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Path("/demo") +public class DemoResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello from Quarkus REST"; + } +} diff --git a/solution/quarkus-appinfo-application/src/main/resources/application.properties b/solution/quarkus-appinfo-application/src/main/resources/application.properties new file mode 100644 index 00000000..9e4556f3 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/main/resources/application.properties @@ -0,0 +1,10 @@ +# Application Name +quarkus.application.name=Demo Application AppInfo + +# build time (only available at build time) +quarkus.appinfo.always-include=true +quarkus.appinfo.record-build-time=true +quarkus.appinfo.built-for=quarkus-training + +# runtime (changeable at runtime) +quarkus.appinfo.run-by=Puzzle ITC GmbH \ No newline at end of file diff --git a/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceIT.java b/solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceIT.java similarity index 52% rename from solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceIT.java rename to solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceIT.java index f70f6505..a1a93cb0 100644 --- a/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceIT.java +++ b/solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceIT.java @@ -1,8 +1,8 @@ -package ch.puzzle.quarkustechlab.reactiverest.consumer.boundary; +package ch.puzzle.quarkustechlab.extensions.appinfo.application.boundary; import io.quarkus.test.junit.QuarkusIntegrationTest; @QuarkusIntegrationTest -public class DataResourceIT extends DataResourceTest { +class DemoResourceIT extends DemoResourceTest { // Execute the same tests but in packaged mode. } diff --git a/solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceTest.java b/solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceTest.java new file mode 100644 index 00000000..a66f5a20 --- /dev/null +++ b/solution/quarkus-appinfo-application/src/test/java/ch/puzzle/quarkustechlab/extensions/appinfo/application/boundary/DemoResourceTest.java @@ -0,0 +1,20 @@ +package ch.puzzle.quarkustechlab.extensions.appinfo.application.boundary; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTest +class DemoResourceTest { + @Test + void testHelloEndpoint() { + given() + .when().get("/demo") + .then() + .statusCode(200) + .body(is("Hello from Quarkus REST")); + } + +} \ No newline at end of file diff --git a/solution/quarkus-cloudevents-consumer/pom.xml b/solution/quarkus-cloudevents-consumer/pom.xml index e5d8e560..564b5966 100644 --- a/solution/quarkus-cloudevents-consumer/pom.xml +++ b/solution/quarkus-cloudevents-consumer/pom.xml @@ -6,15 +6,15 @@ quarkus-cloudevents-consumer 1.0.0-SNAPSHOT - 3.11.0 + 3.12.1 17 UTF-8 UTF-8 quarkus-bom io.quarkus.platform - 3.4.3 + 3.11.1 true - 3.1.2 + 3.2.3 @@ -30,11 +30,11 @@ io.quarkus - quarkus-resteasy-reactive + quarkus-rest io.quarkus - quarkus-smallrye-reactive-messaging-kafka + quarkus-messaging-kafka io.quarkus @@ -142,7 +142,8 @@ false - native + true + false diff --git a/solution/quarkus-cloudevents-producer/pom.xml b/solution/quarkus-cloudevents-producer/pom.xml index cf516bf9..2015843c 100644 --- a/solution/quarkus-cloudevents-producer/pom.xml +++ b/solution/quarkus-cloudevents-producer/pom.xml @@ -6,15 +6,15 @@ quarkus-cloudevents-producer 1.0.0-SNAPSHOT - 3.11.0 + 3.12.1 17 UTF-8 UTF-8 quarkus-bom io.quarkus.platform - 3.4.3 + 3.11.1 true - 3.1.2 + 3.2.3 @@ -30,11 +30,11 @@ io.quarkus - quarkus-resteasy-reactive + quarkus-rest io.quarkus - quarkus-smallrye-reactive-messaging-kafka + quarkus-messaging-kafka io.quarkus @@ -155,7 +155,8 @@ false - native + true + false diff --git a/solution/quarkus-getting-started/pom.xml b/solution/quarkus-getting-started/pom.xml index da848dda..90b31fb1 100644 --- a/solution/quarkus-getting-started/pom.xml +++ b/solution/quarkus-getting-started/pom.xml @@ -6,15 +6,15 @@ quarkus-getting-started 1.0.0-SNAPSHOT - 3.11.0 + 3.12.1 17 UTF-8 UTF-8 quarkus-bom io.quarkus.platform - 3.4.3 + 3.11.1 true - 3.1.2 + 3.2.3 @@ -34,11 +34,11 @@ io.quarkus - quarkus-resteasy-reactive + quarkus-rest io.quarkus - quarkus-resteasy-reactive-jackson + quarkus-rest-jackson io.quarkus @@ -118,7 +118,8 @@ false - native + true + false diff --git a/solution/quarkus-getting-started/src/test/java/ch/puzzle/quarkustechlab/StaticContentTest.java b/solution/quarkus-getting-started/src/test/java/ch/puzzle/quarkustechlab/StaticContentTest.java index cc64ae86..a9248705 100644 --- a/solution/quarkus-getting-started/src/test/java/ch/puzzle/quarkustechlab/StaticContentTest.java +++ b/solution/quarkus-getting-started/src/test/java/ch/puzzle/quarkustechlab/StaticContentTest.java @@ -22,7 +22,7 @@ public class StaticContentTest { public void testIndexHtml() throws Exception { try (InputStream in = url.openStream()) { String contents = readStream(in); - Assertions.assertTrue(contents.contains("getting-started")); + Assertions.assertTrue(contents.contains("<title>quarkus-getting-started")); } } diff --git a/solution/quarkus-metrics-data-consumer/.gitignore b/solution/quarkus-metrics-data-consumer/.gitignore new file mode 100644 index 00000000..8c7863e7 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/.gitignore @@ -0,0 +1,43 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env + +# Plugin directory +/.quarkus/cli/plugins/ diff --git a/solution/quarkus-metrics-data-consumer/.mvn/wrapper/.gitignore b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/.gitignore new file mode 100644 index 00000000..e72f5e8b --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/solution/quarkus-metrics-data-consumer/.mvn/wrapper/MavenWrapperDownloader.java b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 00000000..84d1e60d --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public final class MavenWrapperDownloader +{ + private static final String WRAPPER_VERSION = "3.2.0"; + + private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) ); + + public static void main( String[] args ) + { + log( "Apache Maven Wrapper Downloader " + WRAPPER_VERSION ); + + if ( args.length != 2 ) + { + System.err.println( " - ERROR wrapperUrl or wrapperJarPath parameter missing" ); + System.exit( 1 ); + } + + try + { + log( " - Downloader started" ); + final URL wrapperUrl = new URL( args[0] ); + final String jarPath = args[1].replace( "..", "" ); // Sanitize path + final Path wrapperJarPath = Paths.get( jarPath ).toAbsolutePath().normalize(); + downloadFileFromURL( wrapperUrl, wrapperJarPath ); + log( "Done" ); + } + catch ( IOException e ) + { + System.err.println( "- Error downloading: " + e.getMessage() ); + if ( VERBOSE ) + { + e.printStackTrace(); + } + System.exit( 1 ); + } + } + + private static void downloadFileFromURL( URL wrapperUrl, Path wrapperJarPath ) + throws IOException + { + log( " - Downloading to: " + wrapperJarPath ); + if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null ) + { + final String username = System.getenv( "MVNW_USERNAME" ); + final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray(); + Authenticator.setDefault( new Authenticator() + { + @Override + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication( username, password ); + } + } ); + } + try ( InputStream inStream = wrapperUrl.openStream() ) + { + Files.copy( inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING ); + } + log( " - Downloader complete" ); + } + + private static void log( String msg ) + { + if ( VERBOSE ) + { + System.out.println( msg ); + } + } + +} diff --git a/solution/quarkus-metrics-data-consumer/.mvn/wrapper/maven-wrapper.properties b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..6d3a5665 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/solution/quarkus-metrics-data-consumer/README.md b/solution/quarkus-metrics-data-consumer/README.md new file mode 100644 index 00000000..b589bfa3 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/README.md @@ -0,0 +1,59 @@ +# quarkus-metrics-data-consumer + +This project uses Quarkus, the Supersonic Subatomic Java Framework. + +If you want to learn more about Quarkus, please visit its website: https://quarkus.io/ . + +## Running the application in dev mode + +You can run your application in dev mode that enables live coding using: +```shell script +./mvnw compile quarkus:dev +``` + +> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8080/q/dev/. + +## Packaging and running the application + +The application can be packaged using: +```shell script +./mvnw package +``` +It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory. +Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory. + +The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`. + +If you want to build an _über-jar_, execute the following command: +```shell script +./mvnw package -Dquarkus.package.type=uber-jar +``` + +The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`. + +## Creating a native executable + +You can create a native executable using: +```shell script +./mvnw package -Dnative +``` + +Or, if you don't have GraalVM installed, you can run the native executable build in a container using: +```shell script +./mvnw package -Dnative -Dquarkus.native.container-build=true +``` + +You can then execute your native executable with: `./target/quarkus-metrics-data-consumer-1.0.0-SNAPSHOT-runner` + +If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling. + +## Related Guides + + +## Provided Code + +### RESTEasy Reactive + +Easily start your Reactive RESTful Web Services + +[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources) diff --git a/solution/quarkus-metrics-data-consumer/mvnw b/solution/quarkus-metrics-data-consumer/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/solution/quarkus-metrics-data-consumer/mvnw.cmd b/solution/quarkus-metrics-data-consumer/mvnw.cmd new file mode 100644 index 00000000..c4586b56 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/solution/quarkus-metrics-data-consumer/pom.xml b/solution/quarkus-metrics-data-consumer/pom.xml new file mode 100644 index 00000000..22e510af --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>ch.puzzle</groupId> + <artifactId>quarkus-metrics-data-consumer</artifactId> + <version>1.0.0-SNAPSHOT</version> + <properties> + <compiler-plugin.version>3.12.1</compiler-plugin.version> + <maven.compiler.release>17</maven.compiler.release> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> + <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> + <quarkus.platform.version>3.11.1</quarkus.platform.version> + <skipITs>true</skipITs> + <surefire-plugin.version>3.2.3</surefire-plugin.version> + </properties> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>${quarkus.platform.group-id}</groupId> + <artifactId>${quarkus.platform.artifact-id}</artifactId> + <version>${quarkus.platform.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-rest-client-jackson</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-arc</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-rest</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-rest-jackson</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-fault-tolerance</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-health</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-micrometer-registry-prometheus</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5-mockito</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>${quarkus.platform.group-id}</groupId> + <artifactId>quarkus-maven-plugin</artifactId> + <version>${quarkus.platform.version}</version> + <extensions>true</extensions> + <executions> + <execution> + <goals> + <goal>build</goal> + <goal>generate-code</goal> + <goal>generate-code-tests</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>${compiler-plugin.version}</version> + <configuration> + <compilerArgs> + <arg>-parameters</arg> + </compilerArgs> + </configuration> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>${surefire-plugin.version}</version> + <configuration> + <systemPropertyVariables> + <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> + <maven.home>${maven.home}</maven.home> + </systemPropertyVariables> + </configuration> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <version>${surefire-plugin.version}</version> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + <configuration> + <systemPropertyVariables> + <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path> + <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> + <maven.home>${maven.home}</maven.home> + </systemPropertyVariables> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>native</id> + <activation> + <property> + <name>native</name> + </property> + </activation> + <properties> + <skipITs>false</skipITs> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> + </properties> + </profile> + </profiles> +</project> diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm new file mode 100644 index 00000000..45c566b9 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm @@ -0,0 +1,97 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/quarkus-metrics-data-consumer-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.16 + +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="-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" ] + diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm-multi b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm-multi new file mode 100644 index 00000000..3152f2a4 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.jvm-multi @@ -0,0 +1,26 @@ +## Stage 1 : build with maven builder image with native capabilities +FROM quay.io/quarkus/ubi-quarkus-native-image:22.3-java17 AS build +COPY --chown=quarkus:quarkus mvnw /code/mvnw +COPY --chown=quarkus:quarkus .mvn /code/.mvn +COPY --chown=quarkus:quarkus pom.xml /code/ +USER quarkus +WORKDIR /code +RUN ./mvnw -B org.apache.maven.plugins:maven-dependency-plugin:3.6.0:go-offline +COPY src /code/src +RUN ./mvnw package + +## Stage 2 : create the docker final image +FROM registry.access.redhat.com/ubi8/openjdk-17:1.16 + +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 --from=build /code/target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 --from=build /code/target/quarkus-app/*.jar /deployments/ +COPY --chown=185 --from=build /code/target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 --from=build /code/target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" \ No newline at end of file diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.legacy-jar b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 00000000..fa26bef4 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,93 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/quarkus-metrics-data-consumer-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.16 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS="-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" ] diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.multistage b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.multistage new file mode 100644 index 00000000..7b33fe7d --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.multistage @@ -0,0 +1,25 @@ +FROM quay.io/quarkus/ubi-quarkus-native-image:22.3-java17 AS build +COPY --chown=quarkus:quarkus mvnw /code/mvnw +COPY --chown=quarkus:quarkus .mvn /code/.mvn +COPY --chown=quarkus:quarkus pom.xml /code/ +USER quarkus +WORKDIR /code +RUN ./mvnw -B org.apache.maven.plugins:maven-dependency-plugin:3.6.0:go-offline +COPY src /code/src +RUN ./mvnw package -Pnative + +## Stage 2 : create the docker final image +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +COPY --from=build /code/target/*-runner /work/application + +# set up permissions for user `1001` +RUN chmod 775 /work /work/application \ + && chown -R 1001 /work \ + && chmod -R "g+rwX" /work \ + && chown -R 1001:root /work + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native new file mode 100644 index 00000000..11fbc64d --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/quarkus-metrics-data-consumer . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native-micro b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native-micro new file mode 100644 index 00000000..e2c9626f --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,30 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/quarkus-metrics-data-consumer . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/quarkus-metrics-data-consumer +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResource.java b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResource.java new file mode 100644 index 00000000..1364d878 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResource.java @@ -0,0 +1,47 @@ +package ch.puzzle.quarkustechlab.restconsumer.boundary; + +import ch.puzzle.quarkustechlab.restconsumer.control.HealthService; +import io.micrometer.core.annotation.Timed; +import io.micrometer.core.instrument.MeterRegistry; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import ch.puzzle.quarkustechlab.restconsumer.entity.SensorMeasurement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.function.Supplier; + +@Path("/data") +public class DataConsumerResource { + + private static final Logger logger = LoggerFactory.getLogger(DataConsumerResource.class); + + private final MeterRegistry registry; + + @RestClient + DataProducerService dataProducerService; + + @Inject + HealthService healthService; + + public DataConsumerResource(MeterRegistry registry) { + this.registry = registry; + } + + @GET + @Timed + @Produces(MediaType.APPLICATION_JSON) + public SensorMeasurement getData() { + logger.info("Collecting data from producer"); + Supplier<SensorMeasurement> supplier = () -> dataProducerService.getSensorMeasurement(); + SensorMeasurement sensorMeasurement = registry.timer("REST_call_data").wrap(supplier).get(); + logger.info("Returning data"); + healthService.registerMessageFetch(); + return sensorMeasurement; + } +} diff --git a/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataProducerService.java b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataProducerService.java new file mode 100644 index 00000000..667a2501 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataProducerService.java @@ -0,0 +1,25 @@ +package ch.puzzle.quarkustechlab.restconsumer.boundary; + +import ch.puzzle.quarkustechlab.restconsumer.entity.SensorMeasurement; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.faulttolerance.Fallback; +import org.eclipse.microprofile.faulttolerance.Timeout; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +@Path("/data") +@RegisterRestClient(configKey = "data-producer-api") +public interface DataProducerService { + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Timeout(500) + @Fallback(fallbackMethod = "getDefaultMeasurement") + SensorMeasurement getSensorMeasurement(); + + default SensorMeasurement getDefaultMeasurement() { + return new SensorMeasurement(); + } +} diff --git a/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/HealthService.java b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/HealthService.java new file mode 100644 index 00000000..d0250217 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/HealthService.java @@ -0,0 +1,19 @@ +package ch.puzzle.quarkustechlab.restconsumer.control; + +import jakarta.enterprise.context.ApplicationScoped; + +import java.time.Instant; + +@ApplicationScoped +public class HealthService { + + Instant lastMessageTime; + + public void registerMessageFetch() { + this.lastMessageTime = Instant.now(); + } + + public Instant getLastMessageTime() { + return lastMessageTime; + } +} \ No newline at end of file diff --git a/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/RecentMessageHealthCheck.java b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/RecentMessageHealthCheck.java new file mode 100644 index 00000000..9f9e6e3f --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/control/RecentMessageHealthCheck.java @@ -0,0 +1,33 @@ +package ch.puzzle.quarkustechlab.restconsumer.control; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; +import org.eclipse.microprofile.health.Liveness; + +import java.time.Instant; + +@Liveness +@ApplicationScoped +public class RecentMessageHealthCheck implements HealthCheck { + + @Inject + HealthService healthService; + + @Override + public HealthCheckResponse call() { + Instant lastMessageTime = healthService.getLastMessageTime(); + + HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.named("Last message check") + .status(lastMessageTime == null || ((lastMessageTime.toEpochMilli() + 60000) >= Instant.now().toEpochMilli())); + + if(lastMessageTime != null) { + responseBuilder.withData("lastMessageTime", lastMessageTime.toEpochMilli()) + .withData("ageInMs", (Instant.now().toEpochMilli() - lastMessageTime.toEpochMilli())); + } + + return responseBuilder.build(); + } +} \ No newline at end of file diff --git a/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/entity/SensorMeasurement.java b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/entity/SensorMeasurement.java new file mode 100644 index 00000000..f97a6c22 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/java/ch/puzzle/quarkustechlab/restconsumer/entity/SensorMeasurement.java @@ -0,0 +1,10 @@ +package ch.puzzle.quarkustechlab.restconsumer.entity; + +public class SensorMeasurement { + + public Double data; + + public SensorMeasurement() { + } +} + diff --git a/solution/quarkus-metrics-data-consumer/src/main/resources/application.properties b/solution/quarkus-metrics-data-consumer/src/main/resources/application.properties new file mode 100644 index 00000000..d003bc37 --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/main/resources/application.properties @@ -0,0 +1,3 @@ +quarkus.http.port=8081 +quarkus.rest-client.data-producer-api.url=http://localhost:8080 +quarkus.arc.dev-mode.monitoring-enabled=true diff --git a/solution/quarkus-metrics-data-consumer/src/test/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResourceTest.java b/solution/quarkus-metrics-data-consumer/src/test/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResourceTest.java new file mode 100644 index 00000000..de20e9db --- /dev/null +++ b/solution/quarkus-metrics-data-consumer/src/test/java/ch/puzzle/quarkustechlab/restconsumer/boundary/DataConsumerResourceTest.java @@ -0,0 +1,31 @@ +package ch.puzzle.quarkustechlab.restconsumer.boundary; + +import ch.puzzle.quarkustechlab.restconsumer.entity.SensorMeasurement; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.InjectMock; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.hamcrest.CoreMatchers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static io.restassured.RestAssured.when; + +@QuarkusTest +class DataConsumerResourceTest { + + @RestClient + @InjectMock + DataProducerService dataProducerService; + + @Test + @DisplayName("should return data at /data") + public void dataTest() { + Mockito.when(dataProducerService.getSensorMeasurement()).thenReturn(new SensorMeasurement()); + when().get("/data") + .then() + .statusCode(200) + .body(CoreMatchers.isA(String.class)); + Mockito.verify(dataProducerService, Mockito.times(1)).getSensorMeasurement(); + } +} \ No newline at end of file diff --git a/solution/quarkus-metrics-data-producer/pom.xml b/solution/quarkus-metrics-data-producer/pom.xml index 6cd6dd85..861dad9b 100644 --- a/solution/quarkus-metrics-data-producer/pom.xml +++ b/solution/quarkus-metrics-data-producer/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-metrics-data-producer</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.2</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -34,7 +34,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> + <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -42,7 +42,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jackson</artifactId> + <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -135,7 +135,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/quarkus-opentelemetry-jaeger/pom.xml b/solution/quarkus-opentelemetry-jaeger/pom.xml index d607f7c2..78a767e7 100644 --- a/solution/quarkus-opentelemetry-jaeger/pom.xml +++ b/solution/quarkus-opentelemetry-jaeger/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-opentelemetry-jaeger</artifactId> <version>1.0.0</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.2</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -30,7 +30,7 @@ <dependencies> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> + <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -118,7 +118,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/quarkus-reactive-messaging-consumer/pom.xml b/solution/quarkus-reactive-messaging-consumer/pom.xml index 502e3241..281edcc9 100644 --- a/solution/quarkus-reactive-messaging-consumer/pom.xml +++ b/solution/quarkus-reactive-messaging-consumer/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-reactive-messaging-consumer</artifactId> <version>1.0.0</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.3</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -30,15 +30,15 @@ <dependencies> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jackson</artifactId> + <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jsonb</artifactId> + <artifactId>quarkus-rest-jsonb</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId> + <artifactId>quarkus-messaging-kafka</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -46,7 +46,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> + <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -126,7 +126,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/quarkus-reactive-messaging-producer/pom.xml b/solution/quarkus-reactive-messaging-producer/pom.xml index accfec9d..f6893bc4 100644 --- a/solution/quarkus-reactive-messaging-producer/pom.xml +++ b/solution/quarkus-reactive-messaging-producer/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-reactive-messaging-producer</artifactId> <version>1.0.0</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.3</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -38,7 +38,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId> + <artifactId>quarkus-messaging-kafka</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -117,7 +117,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/quarkus-reactive-rest-consumer/pom.xml b/solution/quarkus-reactive-rest-consumer/pom.xml index 94e71af0..24641544 100644 --- a/solution/quarkus-reactive-rest-consumer/pom.xml +++ b/solution/quarkus-reactive-rest-consumer/pom.xml @@ -12,7 +12,7 @@ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.10.0</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> @@ -44,6 +44,11 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-rest</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5-mockito</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> diff --git a/solution/quarkus-reactive-rest-consumer/src/main/resources/application.properties b/solution/quarkus-reactive-rest-consumer/src/main/resources/application.properties index 167b40af..39df1b7f 100644 --- a/solution/quarkus-reactive-rest-consumer/src/main/resources/application.properties +++ b/solution/quarkus-reactive-rest-consumer/src/main/resources/application.properties @@ -1,4 +1,7 @@ quarkus.http.port=8081 quarkus.rest-client.data-service.url=http://localhost:8080 -quarkus.rest-client.data-service.scope=jakarta.inject.Singleton \ No newline at end of file +quarkus.rest-client.data-service.scope=jakarta.inject.Singleton + +# Used for mocking +%test.quarkus.rest-client.data-service.scope=jakarta.enterprise.context.ApplicationScoped \ No newline at end of file diff --git a/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceTest.java b/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceTest.java index a41be4d9..869c8576 100644 --- a/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceTest.java +++ b/solution/quarkus-reactive-rest-consumer/src/test/java/ch/puzzle/quarkustechlab/reactiverest/consumer/boundary/DataResourceTest.java @@ -1,7 +1,18 @@ package ch.puzzle.quarkustechlab.reactiverest.consumer.boundary; +import ch.puzzle.quarkustechlab.reactiverest.consumer.entity.SensorMeasurement; +import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; +import io.smallrye.mutiny.Uni; +import org.eclipse.microprofile.rest.client.inject.RestClient; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @@ -9,13 +20,27 @@ @QuarkusTest public class DataResourceTest { + @InjectMock + @RestClient + DataService mock; + @Test public void testHelloEndpoint() { + LocalDate localDate = LocalDate.parse("2024-01-01"); + LocalDateTime localDateTime = localDate.atStartOfDay(); + Instant instant = localDateTime.toInstant(ZoneOffset.UTC); + + SensorMeasurement measurement = new SensorMeasurement(); + measurement.id = 1L; + measurement.data = 3.5d; + measurement.time = instant; + + Mockito.when(mock.findAll()).thenReturn(Uni.createFrom().item(List.of(measurement))); + given() - .when().get("/hello") + .when().get("/data") .then() .statusCode(200) - .body(is("Hello from RESTEasy Reactive")); + .body(is("[{\"id\":1,\"data\":3.5,\"time\":\"2024-01-01T00:00:00Z\"}]")); } - } \ No newline at end of file diff --git a/solution/quarkus-reactive-rest-producer/pom.xml b/solution/quarkus-reactive-rest-producer/pom.xml index 714e402f..860e3c64 100644 --- a/solution/quarkus-reactive-rest-producer/pom.xml +++ b/solution/quarkus-reactive-rest-producer/pom.xml @@ -12,7 +12,7 @@ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.10.0</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> diff --git a/solution/quarkus-rest-data-consumer/pom.xml b/solution/quarkus-rest-data-consumer/pom.xml index 49fdc45e..ea848909 100644 --- a/solution/quarkus-rest-data-consumer/pom.xml +++ b/solution/quarkus-rest-data-consumer/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-rest-data-consumer</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.2</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -30,7 +30,7 @@ <dependencies> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-rest-client-reactive-jackson</artifactId> + <artifactId>quarkus-rest-client-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -38,11 +38,11 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> + <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jackson</artifactId> + <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -135,7 +135,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/quarkus-rest-data-producer/pom.xml b/solution/quarkus-rest-data-producer/pom.xml index fea0b4df..d7952ac7 100644 --- a/solution/quarkus-rest-data-producer/pom.xml +++ b/solution/quarkus-rest-data-producer/pom.xml @@ -6,15 +6,15 @@ <artifactId>quarkus-rest-data-producer</artifactId> <version>1.0.0-SNAPSHOT</version> <properties> - <compiler-plugin.version>3.11.0</compiler-plugin.version> + <compiler-plugin.version>3.12.1</compiler-plugin.version> <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> - <quarkus.platform.version>3.4.2</quarkus.platform.version> + <quarkus.platform.version>3.11.1</quarkus.platform.version> <skipITs>true</skipITs> - <surefire-plugin.version>3.1.2</surefire-plugin.version> + <surefire-plugin.version>3.2.3</surefire-plugin.version> </properties> <dependencyManagement> <dependencies> @@ -34,7 +34,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive</artifactId> + <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -42,7 +42,7 @@ </dependency> <dependency> <groupId>io.quarkus</groupId> - <artifactId>quarkus-resteasy-reactive-jackson</artifactId> + <artifactId>quarkus-rest-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> @@ -131,7 +131,8 @@ </activation> <properties> <skipITs>false</skipITs> - <quarkus.package.type>native</quarkus.package.type> + <quarkus.native.enabled>true</quarkus.native.enabled> + <quarkus.package.jar.enabled>false</quarkus.package.jar.enabled> </properties> </profile> </profiles> diff --git a/solution/techlab-extension-appinfo/pom.xml b/solution/techlab-extension-appinfo/pom.xml index 69d1e1a0..97affff4 100644 --- a/solution/techlab-extension-appinfo/pom.xml +++ b/solution/techlab-extension-appinfo/pom.xml @@ -14,10 +14,10 @@ <properties> <compiler-plugin.version>3.11.0</compiler-plugin.version> <failsafe-plugin.version>${surefire-plugin.version}</failsafe-plugin.version> - <maven.compiler.release>11</maven.compiler.release> + <maven.compiler.release>17</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> - <quarkus.version>3.4.3</quarkus.version> + <quarkus.version>3.11.1</quarkus.version> <surefire-plugin.version>3.0.0</surefire-plugin.version> </properties> <dependencyManagement> diff --git a/update-quarkus.sh b/update-quarkus.sh new file mode 100755 index 00000000..41886761 --- /dev/null +++ b/update-quarkus.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +set -euo pipefail + +version=unset +project=unset +diffonly=unset + +usage() { +>&2 cat << EOF +Usage: $0 + [ -p | --project input (e.g. quarkus-rest-data-consumer) ] + [ -v | --version input (e.g. 3.11.0) ] + [ -d | --diff ] + [ -h | --help ] +EOF +exit 1 +} + +# Printing Git DIFF +run_diff() { + for PROJ in ${PROJECTS[@]}; do + echo -e "\n\n----------------------------------------------------------" + echo -e "Project Git Diff: $PROJ" + echo -e "----------------------------------------------------------" + cat ${ROOT_PWD}/${DIFF_DIR}/${PROJ}_git.log + echo -e "---\n" + done +} + +args=$(getopt -a -o p:v:dh --long project:,version:,diff,help -- "$@") +if [[ $? -gt 0 ]]; then + usage +fi + +eval set -- ${args} +while : +do + case $1 in + -p | --project) project=$2 ; shift 2 ;; + -v | --version) version=$2 ; shift 2 ;; + -d | --diff) diffonly=yes ; shift ;; + -h | --help) usage ; shift ;; + # -- means the end of the arguments; drop this, and break out of the while loop + --) shift; break ;; + *) >&2 echo Unsupported option: $1 + usage ;; + esac +done + +PROJECTS=( + dev-services + quarkus-getting-started + quarkus-rest-data-consumer + quarkus-rest-data-producer + quarkus-reactive-rest-consumer + quarkus-reactive-rest-producer + quarkus-metrics-data-producer + quarkus-metrics-data-consumer + quarkus-opentelemetry-jaeger + quarkus-reactive-messaging-consumer + quarkus-reactive-messaging-producer + quarkus-cloudevents-consumer + quarkus-cloudevents-producer + techlab-extension-appinfo + quarkus-appinfo-application +) +SOLUTION_DIR="solution" +DIFF_DIR_PREFIX="diff-update" +ROOT_PWD=`pwd` + + +if [ "${project}" == "unset" ]; then + echo "Using default project list"; +else + echo "Using project $project"; + if [ ! -d ${SOLUTION_DIR}/${project} ]; then + echo "Project $project specified but does not exist." + exit 1 + fi + PROJECTS=("$project") +fi + +if [ "${version}" == "unset" ]; then + echo "Quarkus version not specified"; + version=`quarkus --version` +fi + +DIFF_DIR=${DIFF_DIR_PREFIX}/${version} +TS=`date +%Y%m%d%H%M%S` +echo "Upgrade Quarkus version: $version" +echo "Latest Quarkus version: `quarkus --version`" +echo "Diff output directory: ${DIFF_DIR}" +echo "Run Timestamp: $TS" + +if [ "${diffonly}" == "yes" ]; then + echo "Running diff only"; + run_diff + exit +fi + +if [ ! -d $DIFF_DIR ]; then + echo "Creating ${DIFF_DIR}" + mkdir -p $DIFF_DIR +fi + +for PROJ in ${PROJECTS[@]}; do + cd ${ROOT_PWD}/${SOLUTION_DIR}/${PROJ} + echo "Updating Project ${SOLUTION_DIR}/${PROJ}" + quarkus update --platform-version=$version + + echo "Writing git diff to: ${ROOT_PWD}/${DIFF_DIR}/${PROJ}.log" + git diff . > ${ROOT_PWD}/${DIFF_DIR}/${PROJ}_git.log 2>&1 + if [[ "${PROJ}" == *"extension"* ]]; then + echo "$PROJ is an quarkus extension. Running maven package install." + mvn clean package install > ${ROOT_PWD}/${DIFF_DIR}/${PROJ}_maven.log 2>&1 + else + echo "$PROJ is a quarkus application. Running maven test" + ./mvnw clean test > ${ROOT_PWD}/${DIFF_DIR}/${PROJ}_maven.log 2>&1 + fi + + if [[ "$?" -ne 0 ]]; then + echo 'Running tests failed.'; + exit $rc + fi + + echo "Generating dependencies csv for ${SOLUTION_DIR}/${PROJ}/pom.xml" + xsltproc ${ROOT_PWD}/dependencies.xsl pom.xml > dependencies.csv + + cd $ROOT_PWD +done + +run_diff + + +echo "all done." + +