mirror of
https://github.com/jlengrand/quarkus-workshop.git
synced 2026-03-10 08:41:21 +00:00
Update to Quarkus 0.27.0
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
= The Basics
|
||||
:experimental:
|
||||
|
||||
In this step, you will create a straightforward application serving a `hello` endpoint. To demonstrate dependency injection this endpoint uses a `greeting` bean.
|
||||
In this step, you will create a straightforward application serving a `hello` endpoint. To demonstrate dependency injection this endpoint uses a `greeting` bean.
|
||||
|
||||
image::arch.png[arch,800]
|
||||
|
||||
@@ -91,7 +91,7 @@ In Che, select the _Command Palette_ by clicking on its icon in the upper right,
|
||||
|
||||
image::runlocally.png[runlocal,800]
|
||||
|
||||
This will compile and run the app using `mvn compile quarkus:dev` in a Terminal window.
|
||||
This will compile and run the app using `mvn compile quarkus:dev` in a Terminal window. Leave this terminal window open throughout the lab! You can complete the entire lab without shutting down Quarkus Live Coding mode. This is very useful for quick expermentation.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -133,7 +133,7 @@ Wow, how cool is that? Supersonic Subatomic live reload! Go ahead and change it
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
`quarkus:dev` runs Quarkus in development mode. This enables live reload with background compilation, which means that when you modify your Java files your resource files and refresh your browser these changes will automatically take effect.
|
||||
`quarkus:dev` runs Quarkus in development mode. This enables live reload with background compilation, which means that when you modify your Java files your resource files and refresh your browser these changes will automatically take effect.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
@@ -141,12 +141,6 @@ Wow, how cool is that? Supersonic Subatomic live reload! Go ahead and change it
|
||||
This will also listen for a debugger on port `5005`. If you want to wait for the debugger to attach before running you can pass `-Ddebug` on the command line. If you don’t want the debugger at all you can use `-Ddebug=false`. We'll use this later.
|
||||
====
|
||||
|
||||
### Stop the previous application
|
||||
|
||||
Let's stop the original application so we can package and re-run it as an executable JAR. In the terminal, press kbd:[CTRL+C] to stop the application (**some browsers don't support this command** - simply close the Terminal using the kbd:[X] button on the tab, which will also kill the running app - remember to click _OK_ to the pop-up dialog about terminating the running process.)
|
||||
|
||||
image::kill.png[kill,800]
|
||||
|
||||
### Package the app
|
||||
|
||||
Quarkus apps can be packaged as an executable JAR file or a native binary. We'll cover native binaries later, so for now, let's package as an executable JAR.
|
||||
@@ -175,14 +169,19 @@ Run the packaged application. In a Terminal, run the following command:
|
||||
|
||||
[source, sh, role="copypaste"]
|
||||
----
|
||||
java -jar target/*-runner.jar
|
||||
java -Dquarkus.http.port=8081 -jar target/*-runner.jar
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
We use `-Dquarkus.http.port=8081` to avoid conflicting with port `8080` used for Live Coding mode
|
||||
====
|
||||
|
||||
With the app running, open a separate terminal window, and ensure the app _is_ running by executing a `curl` command:
|
||||
|
||||
[source, sh, role="copypaste"]
|
||||
----
|
||||
curl http://localhost:8080/hello
|
||||
curl http://localhost:8081/hello
|
||||
----
|
||||
|
||||
You should see:
|
||||
@@ -193,10 +192,10 @@ hola
|
||||
|
||||
## Cleanup
|
||||
|
||||
Go back to the terminal in which you ran the app and stop the app by pressing kbd:[CTRL+C] (or close the Terminal).
|
||||
Go back to the terminal in which you ran the app with `java -jar` and stop the app by pressing kbd:[CTRL+C] (or close the Terminal). Be sure not to close the "Live Coding" terminal!
|
||||
|
||||
## Congratulations!
|
||||
|
||||
You've seen how to build a basic app, package it as an executable JAR and start it up very quickly. The JAR file can be used like any other executable JAR file (e.g. running it as-is, packaging as a Linux container, etc.)
|
||||
|
||||
In the next step we'll inject a custom bean to showcase Quarkus' CDI capabilities.
|
||||
In the next step we'll inject a custom bean to showcase Quarkus' CDI capabilities.
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
|
||||
In the previous step you created a basic RESTful Java application with Quarkus. In this step we'll add a custom bean using dependency injection (DI). Quarkus DI solution is based on the http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html[Contexts and Dependency Injection for Java 2.0 specification,window=_blank]. Because of the dynamic nature of some CDI APIs which conflict with native compilation, only a subset of the CDI features are implemented - see also the https://quarkus.io/guides/cdi-reference#supported_features[list,window=_blank] of supported features.
|
||||
|
||||
== Start Live Coding
|
||||
|
||||
Once again, run the app in _dev_ mode by using the command palette and selecting **Start Live Coding**. We'll leave it running for a while as we make changes.
|
||||
|
||||
== Add Custom Bean
|
||||
|
||||
Let’s modify the application and add a companion bean. In CodeReady, right-click on the `org.acme.people.service` package in the project browser and select _New_ --> _Java Class_. Name the class `GreetingService`.
|
||||
@@ -72,7 +68,7 @@ If you do not get red squigglies, or you can't make them disappear, try to close
|
||||
|
||||
== Inspect the results
|
||||
|
||||
Check that it works as expected by loading the new endpoint using the Preview URL and adding the `/hello/greeting/quarkus` to the end of the URL, just as you did before.
|
||||
Check that it works as expected by loading the new endpoint using the same blue Preview URL and adding the `/hello/greeting/quarkus` to the end of the URL, just as you did before.
|
||||
|
||||
Note we are exercising our new bean using the `/hello/greeting/quarkus` endpoint, and you should see `hello quarkus from <hostname>`.
|
||||
|
||||
@@ -84,5 +80,3 @@ In this case, the hostname is the hostname from the pod the app is running on wi
|
||||
== Congratulations!
|
||||
|
||||
It's a familiar CDI-based environment for you Enterprise Java developers out there, with powerful mechanisms to reload your code _as you type_ (or very close to realtime). In the next step, we'll create some tests for our app, which should also be familiar to _all_ developers.
|
||||
|
||||
Keep the Live Coding mode on (if you've stopped the app, just run **Start Live Coding** again from the command palette).
|
||||
|
||||
@@ -33,7 +33,7 @@ This will add the extension below to your `pom.xml`:
|
||||
</dependency>
|
||||
----
|
||||
|
||||
Next, Run the app once more by using the command palette and select **Start Live Coding** to start the app up in dev local mode. With no code, Quarkus still provides a default health check which may be enough for you if all you need is to know the app started. Try to access the `/health/ready` endpoint on the Terminal:
|
||||
With no code, Quarkus still provides a default health check which may be enough for you if all you need is to know the app started. Try to access the `/health/ready` endpoint on the Terminal:
|
||||
|
||||
[source, sh, role="copypaste"]
|
||||
----
|
||||
@@ -210,10 +210,17 @@ Re-run the health check test:
|
||||
|
||||
[source, sh, role="copypaste"]
|
||||
----
|
||||
curl http://localhost:8080/health/live
|
||||
curl -i http://localhost:8080/health/live
|
||||
----
|
||||
|
||||
The overall outcome should be `DOWN` and you should see in the `checks` array the newly added Database connection health check which is down and the error message explaining why it failed:
|
||||
You should see at the beginning the HTTP response:
|
||||
|
||||
[source,none]
|
||||
----
|
||||
HTTP/1.1 503 Service Unavailable
|
||||
----
|
||||
|
||||
And the returned content should begin with `"status": "DOWN"` and you should see in the `checks` array the newly added Database connection health check which is down and the error message explaining why it failed:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
@@ -226,7 +233,15 @@ The overall outcome should be `DOWN` and you should see in the `checks` array th
|
||||
},
|
||||
----
|
||||
|
||||
We shouldn’t leave this application with a health check in DOWN state. Because we are running Quarkus dev mode, add `database.up=true` to the end of the `src/main/resources/application.properties` file and rerun the health check test again — it should be `UP`!
|
||||
== Fix Health Check
|
||||
We shouldn’t leave this application with a health check in DOWN state. Because we are running Quarkus dev mode, add this to to the end of the `src/main/resources/application.properties` file:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
database.up=true
|
||||
----
|
||||
|
||||
And access again using the same `curl` command — it should be `UP`!
|
||||
|
||||
== Accessing liveness and readiness separately
|
||||
|
||||
@@ -424,7 +439,7 @@ curl http://localhost:8080/hello
|
||||
|
||||
produces `hello quarkus!`.
|
||||
|
||||
Next, kbd:[CTRL+C] it (or close the "Start Live Coding" Terminal window). We need to re-build the app as an executable JAR so it runs with the `prod` profile.
|
||||
Next, let's re-build the app as an executable JAR (which will run with the `prod` profile active).
|
||||
|
||||
Build an executable JAR just as before using the command palette and choosing **Create Executable JAR**.
|
||||
|
||||
@@ -432,7 +447,7 @@ Next, open a new Terminal window and run the the app:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
java -jar target/*-runner.jar
|
||||
java -Dquarkus.http.port=8081 -jar target/*-runner.jar
|
||||
----
|
||||
|
||||
Notice we did not specify any Quarkus profile. When not running in dev mode (`mvn quarkus:dev`), and not running in test mode (`mvn verify`), then the default profile is `prod`.
|
||||
@@ -441,7 +456,7 @@ While the app is running, open a separate Terminal window and test it by running
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
curl http://localhost:8080/hello
|
||||
curl http://localhost:8081/hello
|
||||
----
|
||||
|
||||
What did you get? You should get `hello production quarkus!` indicating that the `prod` profile was active by default. In other sections in this workshop we'll use this feature to overrride important variables like database credentials.
|
||||
@@ -453,7 +468,7 @@ In this example we read configuration properties from `application.properties`.
|
||||
|
||||
== Cleanup
|
||||
|
||||
Stop the app for now by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs.
|
||||
Stop the app that you ran with `java -jar` by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs. Make sure to leave the `Start Live Coding` terminal window open!
|
||||
|
||||
== Congratulations
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ You can probably spot the bug right away but let's see how you could find this w
|
||||
|
||||
== Attach debugger
|
||||
|
||||
In _Live Coding_ mode, Quarkus apps also listen for debugger connections on port `5005`.
|
||||
In _Live Coding_ mode, Quarkus apps also listen for debugger connections on port `5005`. Your app should still be running.
|
||||
|
||||
To connect to the running app, use the _Run > Edit Debug Configurations_ menu command. In the dialog, Click the `+` button next to `JAVA` to create a new Java debug configuration. You can name it whatever you want, but keep the `localhost` field as-is and select port `5005` in the port selection field:
|
||||
|
||||
@@ -69,7 +69,7 @@ You will see three main sections of the debug view:
|
||||
|
||||
* **Breakpoints** - This lists the breakpoints you've set. Each Breakpoint can be further configured, or selectively disabled, by right-clicking on the breakpoint in the breakpoint list.
|
||||
|
||||
* **Frames** - This is an ordered list of _stack frames_ showing the path through the code from the beginning of the thread to the current location in our code.
|
||||
* **Frames** - This is an ordered list of _stack frames_ showing the path through the code from the beginning of the thread to the current location in our code.
|
||||
|
||||
* **Variables** - Here you can see the value of local variables in the selected stack frame. In our code we have no local variables defined yet, but once we start stepping through the code, newly defined variables (like `len`) will appear here.
|
||||
|
||||
@@ -105,7 +105,7 @@ With the bug fixed, re-trigger the method by running the `curl` command again in
|
||||
curl http://localhost:8080/hello/lastletter/foo
|
||||
----
|
||||
|
||||
The breakpoint will be hit once again. step over the lines to verify the value of `lastLetter` is correct before the method returns. You've fixed the bug!
|
||||
The breakpoint will be hit once again. Step over the lines to verify the value of `lastLetter` is correct before the method returns. You've fixed the bug!
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
@@ -116,12 +116,6 @@ Remove the breakpoint by clicking on the line number again to de-highlight it.
|
||||
|
||||
Click **End Debug Session** button to quit the debugging session.
|
||||
|
||||
Quarkus apps are just like any other Java app, so debugging is straightforward and supported by many IDEs and CLIs out there.
|
||||
|
||||
== Cleanup
|
||||
|
||||
Go back to your Terminals, close any unused ones and stop the Live Coding for now by pressing closing the Terminal window in which the app runs.
|
||||
|
||||
== Congratulations!
|
||||
|
||||
Debugging Quarkus apps is the same as any other Java app. Combined with Live Reload, it makes development quick and (relatively) painless!
|
||||
Quarkus apps are just like any other Java app, so debugging is straightforward and supported by many IDEs and CLIs out there. Combined with Live Reload, it makes development quick and (relatively) painless!
|
||||
|
||||
@@ -7,7 +7,7 @@ OpenShift is a commercially supported distribution of Kubernetes from Red Hat. T
|
||||
|
||||
== Build native image
|
||||
|
||||
Let's rebuild our native image with all our changes thus far. Using the Command Palette, select **Build Native Quarkus App** and wait 3-4 minutes for it to finish (this runs `mvn clean package -Pnative` under the hood).
|
||||
Let's rebuild our native image with all our changes thus far. Using the Command Palette, select **Build Native Quarkus App** and wait 3-4 minutes for it to finish (this runs `mvn package -Pnative` under the hood).
|
||||
|
||||
As you recall, the output of this process is a native Linux binary. Let's deploy it to our cluster.
|
||||
|
||||
@@ -61,7 +61,7 @@ For this scenario, a project has been created for you called `{{ OPENSHIFT_USER_
|
||||
First, create a new _binary_ build within OpenShift
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
oc new-build quay.io/redhat/ubi-quarkus-native-runner --binary --name=people -l app=people
|
||||
oc new-build quay.io/quarkus/ubi-quarkus-native-binary-s2i:19.2.1 --binary --name=people -l app=people
|
||||
----
|
||||
|
||||
You should get a `--> Success` message at the end.
|
||||
|
||||
@@ -32,14 +32,14 @@ Adding an extension is simiarly easy. With Maven, you can add extensions using `
|
||||
|
||||
== Add an extension
|
||||
|
||||
Later on in this lab we'll be using Hibernate with Panache, so let's add that extension here. In the Terminal, run the following command to add the _Hibernate ORM with Panache_ extension to your project:
|
||||
Later on in this lab we'll be using MicroProfile metrics, so let's add that extension here. In the Terminal, run the following command to add the _MicroProfile Metrics_ extension to your project:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
mvn quarkus:add-extension -Dextensions="panache"
|
||||
mvn quarkus:add-extension -Dextensions="metrics"
|
||||
----
|
||||
|
||||
Notice we are using the "short" name `panache` instead of the fully qualified name `io.quarkus:quarkus-hibernate-orm-panache`.
|
||||
Notice we are using the "short" name `metrics` instead of the fully qualified name `io.quarkus:quarkus-smallrye-metrics`.
|
||||
|
||||
The result of this command is a new `<dependency>` added to our `pom.xml` which you can see by looking at the differences using the following command:
|
||||
|
||||
@@ -58,7 +58,7 @@ You will see all the changes to `pom.xml` since you started:
|
||||
+ </dependency>
|
||||
+ <dependency>
|
||||
+ <groupId>io.quarkus</groupId>
|
||||
+ <artifactId>quarkus-hibernate-orm-panache</artifactId>
|
||||
+ <artifactId>quarkus-smallrye-metrics</artifactId>
|
||||
+ </dependency>
|
||||
----
|
||||
|
||||
@@ -73,7 +73,7 @@ INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
During development, when you add new extensions you'll need to stop and re-start Quarkus apps, as it will not monitor the state of `pom.xml` and bring in new dependencies during live-reload mode. So be sure to stop and restart whenever you add new extensions!
|
||||
In Live Coding mode, Quarkus will monitor the state of `pom.xml` and bring in new dependencies. No need to stop and restart!
|
||||
====
|
||||
|
||||
== Writing your own extension
|
||||
|
||||
@@ -38,7 +38,7 @@ image::names.png[names,800]
|
||||
|
||||
The Strimzi operator installs and manages Kafka clusters on Kubernetes. It's been pre-installed for you, so all you have to do is create a Kafka cluster inside your namespace.
|
||||
|
||||
First, open the {{CONSOLE_URL}}[OpenShift Console], log in using your username/password if needed (e.g. `{{ OPENSHIFT_USER_NAME }}`/`{{ OPENSHIFT_USER_PASSWORD }}`) and navigate to your project (`{{ OPENSHIFT_USER_NAME }}-project`). Once there, on the left menu click on _Catalog > Developer Catalog_ and type in `kafka` in the keyword filter box:
|
||||
First, open the {{CONSOLE_URL}}[OpenShift Console,window=_blank], log in using your username/password if needed (e.g. `{{ OPENSHIFT_USER_NAME }}`/`{{ OPENSHIFT_USER_PASSWORD }}`) and navigate to your project (`{{ OPENSHIFT_USER_NAME }}-project`). Once there, on the left menu click on _Catalog > Developer Catalog_ and type in `kafka` in the keyword filter box:
|
||||
|
||||
image::kafkacatalog.png[kafkacatalog,800]
|
||||
|
||||
@@ -275,11 +275,35 @@ More details about this configuration is available on the https://kafka.apache.o
|
||||
What about `my-data-stream`? This is an in-memory stream, not connected to a message broker.
|
||||
====
|
||||
|
||||
== Verify Kafka is running
|
||||
|
||||
Verify that the Kafka and Zookeeper pods finally started up by running this command:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
oc get pods|grep names-cluster
|
||||
----
|
||||
|
||||
You'll should see all pods up and `Running`:
|
||||
|
||||
[source,none]
|
||||
----
|
||||
names-cluster-entity-operator-78686cdd4d-rfkwd 3/3 Running 0 6m50s
|
||||
names-cluster-kafka-0 2/2 Running 0 7m41s
|
||||
names-cluster-kafka-1 2/2 Running 0 7m41s
|
||||
names-cluster-kafka-2 2/2 Running 1 7m41s
|
||||
names-cluster-zookeeper-0 2/2 Running 0 8m31s
|
||||
names-cluster-zookeeper-1 2/2 Running 0 8m31s
|
||||
names-cluster-zookeeper-2 2/2 Running 0 8m31s
|
||||
----
|
||||
|
||||
If some of them are still starting, you'll need to wait for them! Run the `oc get pods | grep names-cluster` command repeatedly until they are running. This should take no more than 1-2 minutes.
|
||||
|
||||
== Rebuild Executable JAR
|
||||
|
||||
Now we are ready to run our application. Using the command palette, select **Create Executable JAR**. You should see a bunch of log output that ends with a `SUCCESS` message.
|
||||
Using the command palette, select **Create Executable JAR**. You should see a bunch of log output that ends with a `SUCCESS` message.
|
||||
|
||||
== Deploy
|
||||
== Deploy to OpenShift
|
||||
|
||||
And now start the build using our executable JAR:
|
||||
|
||||
|
||||
@@ -38,10 +38,6 @@ Quarkus provides 3 Vert.x APIs:
|
||||
|
||||
We're using the _Axle_ variant here, which provides good support for async operations in HTTP resources.
|
||||
|
||||
== Run the app
|
||||
|
||||
Next, Run the app once more by using the command palette and select **Start Live Coding** to start the app up in dev local mode.
|
||||
|
||||
== Create RESTful resource
|
||||
|
||||
We'll start by creating a new asynchronous endpoint. Open the `PersonResource` class and add a new field which will provide access to the Vert.x event bus which is used to pass messages between components:
|
||||
@@ -50,7 +46,7 @@ We'll start by creating a new asynchronous endpoint. Open the `PersonResource` c
|
||||
----
|
||||
@Inject EventBus bus; // <1>
|
||||
----
|
||||
<1> Use _Assistant > Organize Imports_, and be sure to import the correct `EventBus` class - which is `io.vertx.axle.core.eventbus.EventBus`.
|
||||
<1> Use _Assistant > Organize Imports_, and be sure to import the correct `EventBus` class - which is `io.vertx.axle.core.eventbus.EventBus`.
|
||||
|
||||
Next, create two new endpoints in the same class which creates new people in our database given a name, and finds people by their name:
|
||||
|
||||
@@ -136,7 +132,7 @@ public class PersonService {
|
||||
p.eyes = color;
|
||||
p.name = name;
|
||||
Person.persist(p); // <1>
|
||||
return p.name; // <2>
|
||||
return p.name; // <2>
|
||||
}
|
||||
|
||||
}
|
||||
@@ -180,10 +176,6 @@ To better understand, let’s detail how the HTTP request/response has been hand
|
||||
. This response is sent back using the reply mechanism
|
||||
. Once the reply is received by the sender, the content is written to the HTTP response
|
||||
|
||||
== Cleanup
|
||||
|
||||
Stop the app for now by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs.
|
||||
|
||||
== Congratulations!
|
||||
|
||||
In this exercise you learned how Quarkus allows different beans to interact using asynchronous messages. We'll take this to the next level in the next exercise.
|
||||
@@ -74,10 +74,6 @@ mvn quarkus:add-extension -Dextensions="metrics"
|
||||
|
||||
This will add the necessary entries in your `pom.xml` to bring in the Metrics capability. It will import the `smallrye-metrics` extension which is an implementation of the MicroProfile Metrics specification used in Quarkus.
|
||||
|
||||
== Start the app
|
||||
|
||||
Next, Run the app once more by using the command palette and select **Start Live Coding** to start the app up in dev local mode.
|
||||
|
||||
== Test Metrics endpoint
|
||||
|
||||
You will be able to immediately see the raw metrics generated from Quarkus apps. Run this in the Terminal:
|
||||
@@ -167,7 +163,7 @@ Don't forget to import the correct classes as before.
|
||||
|
||||
Now we are ready to run our application on the cluster and look at the generated metrics. Using the command palette, select **Create Executable JAR**. You should see a bunch of log output that ends with a `SUCCESS` message.
|
||||
|
||||
== Deploy
|
||||
== Deploy to OpenShift
|
||||
|
||||
Let's deploy our app to the cluster and see if Prometheus picks up our metrics! To do this, start the container build using our executable JAR:
|
||||
|
||||
@@ -194,7 +190,7 @@ You'll need to trigger the methods that we've instrumented, so first run this co
|
||||
echo; echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/names.html ; echo
|
||||
----
|
||||
|
||||
Within about 15-30 seconds, Prometheus should start scraping the metrics. Run this command to output the URL to the Prometheus GUI:
|
||||
Open that link in your browser. Within about 15-30 seconds, Prometheus should start scraping the metrics. Run this command to output the URL to the Prometheus GUI:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
@@ -343,10 +339,6 @@ image::graffinal.png[final,500]
|
||||
|
||||
Beautiful, and useful! You can add many more metrics to monitor and alert for Quarkus apps using these tools.
|
||||
|
||||
== Cleanup
|
||||
|
||||
Go to the first Terminal tab and press kbd:[CTRL+C] to stop our locally running app (or close the Terminal window).
|
||||
|
||||
== Congratulations!
|
||||
|
||||
This exercise demonstrates how your Quarkus application can utilize the https://github.com/eclipse/microprofile-metrics[MicroProfile Metrics,window=_blank] specification through the SmallRye Metrics extension. You also consumed these metrics using a popular monitoring stack with Prometheus and Grafana.
|
||||
|
||||
@@ -46,18 +46,13 @@ Within the `pom.xml` is the declaration for the Quarkus Maven plugin which conta
|
||||
|
||||
We use a profile because, you will see very soon, packaging the native image takes a few seconds. However, this compilation time is only incurred _once_, as opposed to _every_ time the application starts, which is the case with other approaches for building and executing JARs.
|
||||
|
||||
Create a native executable by once again opening the command palette and choose **Build Native Quarkus App**. This will execute `mvn clean package -Pnative` behind the scenes. The `-Pnative` argument selects the `native` maven profile which invokes the Graal compiler.
|
||||
Create a native executable by once again opening the command palette and choose **Build Native Quarkus App**. This will execute `mvn package -Pnative` behind the scenes. The `-Pnative` argument selects the `native` maven profile which invokes the Graal compiler.
|
||||
|
||||
**This will take about 3-4 minutes to finish. Wait for it!**
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
You can safely ignore any warnings like `Warning: RecomputeFieldValue.FieldOffset automatic substitution failed.` These are harmless and will be removed in future releases of Quarkus.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Since we are on Linux in this environment, and the OS that will eventually run our application is also Linux, we can use our local OS to build the native Quarkus app. If you need to build native Linux binaries when on other OS's like Windows or Mac OS X, you'll need to have Docker installed and then use `mvn clean package -Pnative -Dnative-image.docker-build=true -DskipTests=true`.
|
||||
Since we are on Linux in this environment, and the OS that will eventually run our application is also Linux, we can use our local OS to build the native Quarkus app. If you need to build native Linux binaries when on other OS's like Windows or Mac OS X, you can use `-Dquarkus.native.container-runtime=[podman | docker]`. You'll need either Docker or https://podman.io[Podman,target=_blank] installed depending on which runtime you want to use!
|
||||
====
|
||||
|
||||
In addition to the regular files, the build will produce `target/people-1.0-SNAPSHOT-runner`. This is a native Linux binary. Not a shell script, or a JAR file, but a native binary.
|
||||
@@ -68,14 +63,14 @@ Since our environment here is Linux, you can _just run it_. In the terminal, run
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
target/people-1.0-SNAPSHOT-runner
|
||||
target/people-1.0-SNAPSHOT-runner -Dquarkus.http.port=8081
|
||||
----
|
||||
|
||||
Notice the amazingly fast startup time:
|
||||
|
||||
[source,none,role="copypaste"]
|
||||
----
|
||||
2019-07-10 04:04:11,817 INFO [io.quarkus] (main) Quarkus 0.18.0 started in 0.015s. Listening on: http://[::]:8080
|
||||
2019-07-10 04:04:11,817 INFO [io.quarkus] (main) Quarkus 0.18.0 started in 0.015s. Listening on: http://[::]:8081
|
||||
2019-07-10 04:04:11,818 INFO [io.quarkus] (main) Installed features: [agroal, cdi, jdbc-h2, narayana-jta, resteasy]
|
||||
----
|
||||
|
||||
@@ -106,7 +101,7 @@ Make sure the app is still working as expected (we'll use `curl` this time to ac
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
curl http://localhost:8080/hello/greeting/quarkus
|
||||
curl http://localhost:8081/hello/greeting/quarkus
|
||||
----
|
||||
|
||||
You should see:
|
||||
@@ -120,7 +115,7 @@ Nice!
|
||||
|
||||
== Cleanup
|
||||
|
||||
Go to the first Terminal tab and press kbd:[CTRL+C] to stop our native app (or close the Terminal window).
|
||||
Go to the Terminal in which you ran the native app and press kbd:[CTRL+C] to stop our native app (or close the Terminal window with the `X` button next to its title).
|
||||
|
||||
== Congratulations!
|
||||
|
||||
|
||||
@@ -13,18 +13,18 @@ The MicroProfile OpenAPI specification requires vendors to produce a valid OpenA
|
||||
|
||||
Therefore you as a developer get a _lot_ of functionality out of the box without doing anything. Let's test this out.
|
||||
|
||||
== Out of the box APIs
|
||||
== Add Extension
|
||||
|
||||
If your application is still running from the previous exercise, go back to its terminal and type kbd:[CTRL+C] (or just close the terminal window). We need to add another extension for OpenAPI. Run the following command:
|
||||
We need to add another extension for OpenAPI. Run the following command:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
mvn quarkus:add-extension -Dextensions="openapi,swagger"
|
||||
----
|
||||
|
||||
This will add the necessary extension for using OpenAPI, and a graphical frontend extension called `swagger` which we'll discuss later.
|
||||
This will add the necessary extension for using OpenAPI, and a graphical frontend extension called `swagger` which we'll discuss later. It also enables a new RESTful endpoint in the app accessible at `/openapi`.
|
||||
|
||||
Run the project again, using the command palette and selecting **Start Live Coding** (if you had a previous app running, kbd:[CTRL+C] or close the old Terminal before running it again). This will again run our app. Once it's up and running, access the new `openapi` endpoint using the following command in a Terminal:
|
||||
Access the new endpoint using the following command in a Terminal:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
@@ -65,13 +65,6 @@ paths:
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you do not like the default endpoint location /openapi, you can change it by adding the following configuration to your application.properties:
|
||||
|
||||
[source,none]
|
||||
----
|
||||
quarkus.smallrye-openapi.path=/myapi
|
||||
----
|
||||
|
||||
If you want JSON instead of YAML, use
|
||||
|
||||
[source,sh]
|
||||
@@ -80,7 +73,7 @@ curl -H "Accept: application/json" http://localhost:8080/openapi
|
||||
----
|
||||
====
|
||||
|
||||
So out of the box all your endpoints are fully documented and will be emitted in a programmatic format from the `/openapi` endpoint. CLIs are great, but for quick testing of APIs, wouldn't it be great if that programmatic API was turned into an interactive, forms-based web page for quickly trying out endpoints? That's what _Swagger UI_ does.
|
||||
So out of the box all your endpoints are fully documented and will be emitted in a programmatic format from the `/openapi` endpoint. CLIs are great, but for quick testing of APIs, wouldn't it be great if that programmatic API was turned into an interactive, forms-based web page for quickly trying out endpoints? That's what _Swagger UI_ does.
|
||||
|
||||
== Access Swagger UI
|
||||
|
||||
@@ -95,16 +88,9 @@ If you want to make it available in production too, you can include the followin
|
||||
----
|
||||
quarkus.swagger-ui.always-include=true
|
||||
----
|
||||
|
||||
By default, Swagger UI is accessible at `/swagger-ui`. You can update this path by setting the `quarkus.swagger-ui.path` property in your `application.properties`:
|
||||
|
||||
[source]
|
||||
----
|
||||
quarkus.swagger-ui.path=/my-custom-path
|
||||
----
|
||||
====
|
||||
|
||||
Since our app is already running, click on the _Preview URL_ shown at the top of the terminal (on the _Start Live Coding_ tab). Then, add `/swagger-ui` to the end of the URL to access the Swagger UI and play with your API.
|
||||
Since our app is still running, click on the _Preview URL_ shown at the top of the terminal (on the _Start Live Coding_ tab). Then, add `/swagger-ui` to the end of the URL to access the Swagger UI and play with your API.
|
||||
|
||||
image::swagger.png[swagger,600]
|
||||
|
||||
@@ -124,7 +110,7 @@ Pretty handy way to test out your APIs!
|
||||
|
||||
== Documenting your APIs
|
||||
|
||||
OpenAPI and Swagger are useful tools but they become even more useful to 3rd party consumers of your API if you properly document them. With OpenAPI, you as a developer can add additional annotations right into the code to document them (which is a good practice anyway - when the code changes, the docs can too).
|
||||
OpenAPI and Swagger are useful tools but they become even more useful to 3rd party consumers of your API if you properly document them. With OpenAPI, you as a developer can add additional annotations right into the code to document them (which is a good practice anyway - when the code changes, the docs can too).
|
||||
|
||||
You as an application developer have a few choices in documenting your APIs:
|
||||
|
||||
@@ -189,7 +175,7 @@ The final method should look like:
|
||||
public List<Person> getBeforeYear(
|
||||
@Parameter(description = "Cutoff year for searching for people", required = true, name="year")
|
||||
@PathParam(value = "year") int year) {
|
||||
|
||||
|
||||
return Person.getBeforeYear(year);
|
||||
}
|
||||
----
|
||||
@@ -198,10 +184,6 @@ Now reload the same Swagger UI webpage (and expand the `/person/birth/before/{ye
|
||||
|
||||
image::swaggerparams.png[swaggerparams,600]
|
||||
|
||||
== Cleanup
|
||||
|
||||
Stop the app for now by pressing kbd:[CTRL+C] in the terminal or closing the Terminal window in which the app runs.
|
||||
|
||||
== Congratulations
|
||||
|
||||
In this exercise you learned more about the MicroProfile OpenAPI specification and how to use it to do in-place documentation of your RESTful microservice APIs.
|
||||
|
||||
@@ -21,7 +21,7 @@ You may have wondered why we are using `people` in our Java APIs, and now you'll
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
mvn quarkus:add-extension -Dextensions="panache, h2, jdbc-postgresql, resteasy-jsonb"
|
||||
mvn quarkus:add-extension -Dextensions="hibernate-orm-panache, jdbc-h2, jdbc-postgresql, resteasy-jsonb"
|
||||
----
|
||||
|
||||
We'll use the in-memory H2 database for local testing, and Postgres when we deploy to our production Kubernetes environment. So that's why we have added in the `h2` and `postgresql` extensions. `resteasy-jsonb` is used to encode JSON objects.
|
||||
@@ -47,7 +47,7 @@ These names are known to Quarkus, and you can introduce your own profiles and us
|
||||
|
||||
== Create Entity
|
||||
|
||||
With our extension installed, we can now define our entity using Panache.
|
||||
With our extension installed, we can now define our entity using Panache.
|
||||
|
||||
We'll first need the definition of eye colors, so let's create an `enum`. Create a new java class in `src/main/java/org/acme/people/model` called `EyeColor`, and add the following enum definition:
|
||||
|
||||
@@ -83,7 +83,7 @@ import org.acme.people.model.EyeColor;
|
||||
public class Person extends PanacheEntity {
|
||||
// the person's name
|
||||
public String name;
|
||||
|
||||
|
||||
// the person's birthdate
|
||||
public LocalDate birth;
|
||||
|
||||
@@ -145,7 +145,7 @@ public class PersonResource {
|
||||
}
|
||||
|
||||
// TODO: add basic queries
|
||||
|
||||
|
||||
// TODO: add datatable query
|
||||
|
||||
// TODO: Add lifecycle hook
|
||||
@@ -172,22 +172,14 @@ Add these lines to `import.sql` file you just created:
|
||||
|
||||
[source,sql,role="copypaste"]
|
||||
----
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Farid Ulyanov', to_date('1974-08-15', 'YYYY-MM-dd'), 'BLUE')
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Salvador L. Witcher', to_date('1984-05-24', 'YYYY-MM-dd'), 'BROWN')
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Kim Hu', to_date('1999-04-25', 'YYYY-MM-dd'), 'HAZEL')
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Farid Ulyanov', to_date('1974-08-15', 'YYYY-MM-dd'), 'BLUE');
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Salvador L. Witcher', to_date('1984-05-24', 'YYYY-MM-dd'), 'BROWN');
|
||||
INSERT INTO person(id, name, birth, eyes) VALUES (nextval('hibernate_sequence'), 'Kim Hu', to_date('1999-04-25', 'YYYY-MM-dd'), 'HAZEL');
|
||||
----
|
||||
|
||||
These statements will add some fake people to our database on startup.
|
||||
|
||||
== Run the application
|
||||
|
||||
Now we are ready to run our application. Using the command palette, select **Start Live Coding** (if you had a previous app running, kbd:[CTRL+C] or close the old Terminal before running it again). You should see a bunch of log output that ends with:
|
||||
|
||||
[source, none]
|
||||
----
|
||||
12:56:43,106 INFO [io.quarkus] Quarkus 0.12.0 started in 2.138s. Listening on: http://[::]:8080
|
||||
12:56:43,106 INFO [io.quarkus] Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, narayana-jta, resteasy, resteasy-jsonb]```
|
||||
----
|
||||
== Test the app
|
||||
|
||||
With the app running, let's try out our first RESTful endpoint to retrieve all the sample users. Open up a separate Terminal and issue the following command:
|
||||
|
||||
@@ -222,7 +214,7 @@ We call the endpoint with `curl` then send the output through `jq` to make the o
|
||||
]
|
||||
----
|
||||
|
||||
It's working! We'll leave it running and use Quarkus' Live Reload feature to automatically update our app as we make changes. Note that the `id` and `persistent` fields were added to our entity, but never appear in our query APIs and can be safely ignored most of the time.
|
||||
It's working! Note that the `id` and `persistent` fields were added to our entity, but never appear in our query APIs and can be safely ignored most of the time.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -265,7 +257,7 @@ These two queries will find a list of people in our database based on eye color,
|
||||
All list methods in Panache-based entities (those that extend from `PanacheEntity`) have equivalent stream versions. So `list` has a `stream` variant, `listAll`-->`streamAll` and so on.
|
||||
====
|
||||
|
||||
With our custom entity queries implemented in our `Person` entity class, let's add RESTful endpoints to `PersonResource` to access them.
|
||||
With our custom entity queries implemented in our `Person` entity class, let's add RESTful endpoints to `PersonResource` to access them.
|
||||
|
||||
Open the `PersonResource` class and add two news endpoint under the `//TODO: add basic queries` comment:
|
||||
|
||||
@@ -288,8 +280,6 @@ public List<Person> getBeforeYear(@PathParam(value = "year") int year) {
|
||||
|
||||
== Inspect the results
|
||||
|
||||
Since we still have our app running using `mvn quarkus:dev`, when you make these changes and reload the endpoint, Quarkus will notice all of these changes and live reload them.
|
||||
|
||||
Check that it works as expected by testing the new endpoints. Let's find all the people with `BLUE` eyes. Execute in your Terminal:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
@@ -343,11 +333,9 @@ You should see **two** people born in 1990 or earlier:
|
||||
|
||||
The `Person` entity's superclass comes with lots of super useful static methods and you can add your own in your entity class. Users can just start using your entity `Person` by typing `Person`, and getting completion for all the operations in a single place.
|
||||
|
||||
In the next step we'll show you how Panache can help to adapt entities to high performance frontends, even in the face of millions of records.
|
||||
|
||||
== Add Paging and Filtering
|
||||
|
||||
In the previous step you added a few more custom queries to your entity and the associated RESTful endpoints. In this step we'll build a slightly more complex query including filtering, searching and paging capabilities.
|
||||
In the previous step you added a few more custom queries to your entity and the associated RESTful endpoints. In this step we'll build a slightly more complex query including filtering, searching and paging capabilities.
|
||||
|
||||
=== Showing data in tables
|
||||
|
||||
@@ -363,7 +351,7 @@ https://www.datatables.net/manual/server-side[DataTables documentation,window=_b
|
||||
* `length` - Total number records to return (or less, if there are less records that meet criteria)
|
||||
* `search[value]` - The value of the search box
|
||||
* `draw` - DataTables does asnychronous processing, so this value is sent with each request, expecting it to be returned as-is, so DataTables can piece things back together on the frontend if a user clicks things quickly.
|
||||
|
||||
|
||||
Open the `PersonResource` resource class and add the following code below the `// TODO: add datatable query` comment:
|
||||
|
||||
[source,java,role="copypaste"]
|
||||
@@ -400,7 +388,7 @@ DataTables requires a specific JSON payload to be returned from this, and we've
|
||||
* `recordsFiltered` - Total records that match filtering criteria
|
||||
* `data` - The actual array of records
|
||||
* `error` - Error string, if any
|
||||
|
||||
|
||||
So, in our `PersonResource` endpoint, we'll start with an empty `result` object using the pre-created `DataTable` model. Add this code below the `// TODO: Begin Result` comment:
|
||||
|
||||
[source,java,role="copypaste"]
|
||||
@@ -417,7 +405,7 @@ Next, if the request includes a search parameter, let's take care of that by inc
|
||||
[source,java,role="copypaste"]
|
||||
----
|
||||
PanacheQuery<Person> filteredPeople;
|
||||
|
||||
|
||||
if (searchVal != null && !searchVal.isEmpty()) { // <1>
|
||||
filteredPeople = Person.<Person>find("name like :search",
|
||||
Parameters.with("search", "%" + searchVal + "%"));
|
||||
@@ -440,7 +428,7 @@ result.setRecordsFiltered(filteredPeople.count());
|
||||
result.setData(filteredPeople.list());
|
||||
result.setRecordsTotal(Person.count());
|
||||
|
||||
return result;
|
||||
return result;
|
||||
----
|
||||
|
||||
=== Test the result
|
||||
@@ -480,7 +468,7 @@ The `data`, `draw`, `recordsFiltered` and `recordsTotal` values are what the Dat
|
||||
You often need to execute custom actions when the application starts and clean up everything when the application stops. In this case we'll add an action that will pre-generate a lot of fake data.
|
||||
|
||||
Managed beans (like our `PersonResource`) can listen for lifecycle events by using the `@Observes` annotation on method signatures, which will be called when the associated event occurs.
|
||||
|
||||
|
||||
Open the `PersonResource` resource class and add the following lifecycle listener at the `// TODO: Add lifecycle hook` marker:
|
||||
|
||||
[source,java,role="copypaste"]
|
||||
@@ -513,7 +501,7 @@ curl -s "http://localhost:8080/person/datatable?draw=1&start=0&length=2&search\[
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Adding 1k entries will make startup time artificially high, around 1-2 seconds.
|
||||
Adding 1k entries will make startup time artificially high, around 1-2 seconds.
|
||||
====
|
||||
|
||||
You should get up to 2 records returned, but the total number available should show many more indicating our search found many more, and the total number of records should now be `1003` (the 1k we added plus the 3 original values):
|
||||
@@ -543,13 +531,9 @@ You should get up to 2 records returned, but the total number available should s
|
||||
|
||||
Note the values for `recordsFiltered` (the number of records with the letter `F` in the name), and `recordsTotal`. The value you see for `recordsFiltered` may be different than the above value, since the number of records with an `F` in the name may vary since the data is random. But the `recordsTotal` shows our initial 3 values, plus the 1000 additional values we added in the lifecycle hook code.
|
||||
|
||||
== Cleanup
|
||||
== Deploy to OpenShift
|
||||
|
||||
We're done with development, so go back to the Live Coding tab and press kbd:[CTRL+C] (or just close the tab) to stop our running application.
|
||||
|
||||
== Deploy Postgres database
|
||||
|
||||
Our production environment needs a "real" database so let's deploy a Postgres database. Run the following command to startup a database in our cluster:
|
||||
Our production environment needs a "real" database so let's deploy a Postgres database to OpenShift. Run the following command to startup a database in our cluster:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
@@ -573,7 +557,7 @@ Next, re-define the container build to use the OpenJDK image using these command
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
oc delete bc/people && oc new-build registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.5 --binary --name=people
|
||||
oc delete bc/people && oc new-build registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.6 --binary --name=people
|
||||
----
|
||||
|
||||
And now start the build using our executable JAR:
|
||||
@@ -620,4 +604,4 @@ Skip around a few pages, try some different searches, and notice that the data i
|
||||
|
||||
== Congratulations
|
||||
|
||||
In this exercise you got a glimpse of the power of Quarkus apps when dealing with large amounts of data. There is much more to Quarkus than fast startup times and low resource usage, so keep going!
|
||||
In this exercise you got a glimpse of the power of Quarkus apps when dealing with large amounts of data. There is much more to Quarkus than fast startup times and low resource usage, so keep going!
|
||||
@@ -63,8 +63,8 @@ public class JWTResource {
|
||||
@Inject
|
||||
JsonWebToken jwt; // <2>
|
||||
|
||||
@Inject
|
||||
@Claim(standard = Claims.iss)
|
||||
@Inject
|
||||
@Claim(standard = Claims.iss)
|
||||
Optional<JsonString> issuer; // <3>
|
||||
|
||||
@GET
|
||||
@@ -79,7 +79,7 @@ public class JWTResource {
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/me/admin")
|
||||
@Path("/me/admin")
|
||||
@RolesAllowed("admin")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
public String meJwt(@Context SecurityContext ctx) { // <4>
|
||||
@@ -126,7 +126,7 @@ oc rollout status -w dc/people
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
In this exercise we are short-circuiting typical web authentication flows to illustrate the ease of protecting APIs with Quarkus. In a typical web authentication, users are redirected (via their browser) to a login page, after which a negotiation is performed to retrieve _access tokens_ used on behalf of the user to access protected resources. Here we are doing this manually with `curl`.
|
||||
In this exercise we are **short-circuiting typical web authentication flows** to illustrate the ease of protecting APIs with Quarkus. In a typical web authentication, users are redirected (via their browser) to a login page, after which a negotiation is performed to retrieve _access tokens_ used on behalf of the user to access protected resources. Here we are doing this manually with `curl`.
|
||||
====
|
||||
|
||||
The first thing to do to test any endpoint is obtain an access token from your authentication server in order to access the application resources. We've pre-created a few users in Keycloak for you to use:
|
||||
@@ -170,7 +170,7 @@ export ALICE_TOKEN=$(\
|
||||
-d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
|
||||
) && echo $ALICE_TOKEN
|
||||
----
|
||||
This issues a `curl` command to Keycloak (using `backend-service` credentials which is a special user that is allowed acess to the Keycloak REST API), and fetches a token for Alice using their credentials.
|
||||
This issues a `curl` command to Keycloak (using `backend-service` credentials which is a special user that is allowed acess to the Keycloak REST API), and fetches a token for Alice using their credentials.
|
||||
|
||||
Try out the JWT-secured API as Alice:
|
||||
|
||||
@@ -281,7 +281,7 @@ First, you'll need to enable the Keycloak extension:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
mvn quarkus:add-extension -Dextensions="keycloak"
|
||||
mvn quarkus:add-extension -Dextensions="oidc, keycloak-authorization, resteasy-jsonb"
|
||||
----
|
||||
|
||||
=== Configuring Keycloak
|
||||
@@ -290,13 +290,13 @@ Next, add these to your `application.properties` for Keycloak:
|
||||
|
||||
[source,none,role="copypaste"]
|
||||
----
|
||||
quarkus.keycloak.realm=quarkus
|
||||
quarkus.keycloak.auth-server-url={{ KEYCLOAK_URL }}/auth
|
||||
quarkus.keycloak.resource=backend-service
|
||||
quarkus.keycloak.bearer-only=true
|
||||
quarkus.keycloak.credentials.secret=secret
|
||||
# OIDC config
|
||||
quarkus.oidc.auth-server-url={{ KEYCLOAK_URL }}/auth/realms/quarkus
|
||||
quarkus.oidc.client-id=backend-service
|
||||
quarkus.oidc.credentials.secret=secret
|
||||
|
||||
# Enable Policy Enforcement
|
||||
quarkus.keycloak.policy-enforcer.enable=true
|
||||
quarkus.keycloak.policy-enforcer.enforcement-mode=PERMISSIVE
|
||||
quarkus.keycloak.policy-enforcer.paths.ready.name=Readiness
|
||||
quarkus.keycloak.policy-enforcer.paths.ready.path=/health/ready
|
||||
quarkus.keycloak.policy-enforcer.paths.ready.enforcement-mode=DISABLED
|
||||
@@ -343,7 +343,7 @@ public class KeycloakResource {
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> The `KeycloakSecurityContext` is an object produced by the Keycloak extension that you can use to obtain information from tokens sent to your application.
|
||||
<1> The `KeycloakSecurityContext` is an object produced by the Keycloak extension that you can use to obtain information from tokens sent to your application.
|
||||
<2> Note that we do not use any `@RolesAllowed` or any other instrumentation on the endpoint to specify access policy. It looks like an ordinary endpoint.
|
||||
|
||||
[NOTE]
|
||||
@@ -373,7 +373,7 @@ oc rollout status -w dc/people
|
||||
|
||||
=== Test confidential
|
||||
|
||||
The `/secured/confidential` endpoint is protected with a policy defined in the Keycloak Server. The policy only grants access to the resource if the user is granted with a `confidential` role. The difference here is that the application is delegating the access decision to Keycloak, so no explicit source code instrumentation is required.
|
||||
The `/secured/confidential` endpoint is protected with a policy defined in the Keycloak Server. The policy only grants access to the resource if the user is granted with a `confidential` role. The difference here is that the application is delegating the access decision to Keycloak, so no explicit source code instrumentation is required.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
@@ -382,9 +382,23 @@ Keycloak caches the resource paths that it is protecting, so that every access d
|
||||
|
||||
First make sure even `admin` can't access the endpoint:
|
||||
|
||||
Refresh the admin token (it may have expired):
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
curl -v -X GET \
|
||||
export ADMIN_TOKEN=$(\
|
||||
curl -s -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
|
||||
--user backend-service:secret \
|
||||
-H 'content-type: application/x-www-form-urlencoded' \
|
||||
-d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
|
||||
) && echo $ADMIN_TOKEN
|
||||
----
|
||||
|
||||
And then try to access with it:
|
||||
|
||||
[source,sh,role="copypaste"]
|
||||
----
|
||||
curl -i -X GET \
|
||||
http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/secured/confidential \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
----
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
= Testing
|
||||
:experimental:
|
||||
|
||||
In this step we'll show you how to effectively write functional and unit tests for your Quarkus Apps.
|
||||
In this step we'll show you how to effectively write functional and unit tests for your Quarkus Apps.
|
||||
|
||||
You should already have a completed test written for you, including the correct `pom.xml` setup where you should see 2 test dependencies:
|
||||
|
||||
@@ -72,7 +72,7 @@ The tests will run, and eventually complete. Did you get any errors? You should
|
||||
|
||||
[source, none]
|
||||
----
|
||||
[ERROR] Failures:
|
||||
[ERROR] Failures:
|
||||
[ERROR] GreetingResourceTest.testHelloEndpoint:18 1 expectation failed.
|
||||
Response body doesn't match expectation.
|
||||
Expected: is "hello"
|
||||
@@ -92,7 +92,7 @@ You can configure the port used by tests by configuring `quarkus.http.test-port`
|
||||
quarkus.http.test-port=8083
|
||||
----
|
||||
|
||||
Now re-run the tests and look for
|
||||
Now re-run the tests and look for
|
||||
|
||||
[source, none]
|
||||
----
|
||||
@@ -118,7 +118,7 @@ package org.acme.people;
|
||||
@QuarkusTest
|
||||
public class StaticContentTest {
|
||||
|
||||
@TestHTTPResource("test.html")
|
||||
@TestHTTPResource("test.html")
|
||||
URL url;
|
||||
|
||||
@Test
|
||||
@@ -174,7 +174,7 @@ public class GreetingServiceTest {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("GreetingServiceTest");
|
||||
|
||||
@Inject
|
||||
@Inject
|
||||
GreetingService service;
|
||||
|
||||
@Test
|
||||
@@ -230,7 +230,7 @@ public class TestStereotypeTestCase {
|
||||
|
||||
Quarkus supports the use of mock objects using the CDI `@Alternative` mechanism. To use this simply override the bean you wish to mock with a class in the `src/test/java` directory, and put the `@Alternative` and `@Priority(1)`` annotations on the bean. Alternatively, a convenient `io.quarkus.test.Mock` stereotype annotation could be used. This built-in stereotype declares `@Alternative`, `@Priority(1)` and `@Dependent`.
|
||||
|
||||
Let's mock our existing `GreetingService`. Although our existing service is pretty simple, in the real world the service might have too many dependencies on external systems to be feasible to call directly.
|
||||
Let's mock our existing `GreetingService`. Although our existing service is pretty simple, in the real world the service might have too many dependencies on external systems to be feasible to call directly.
|
||||
|
||||
Create a new class in `src/test/java` in the `org.acme.people` package called `MockGreetingService` with the following code:
|
||||
|
||||
@@ -243,12 +243,12 @@ import org.acme.people.service.GreetingService;
|
||||
import io.quarkus.test.Mock;
|
||||
|
||||
@Mock
|
||||
@ApplicationScoped
|
||||
@ApplicationScoped
|
||||
public class MockGreetingService extends GreetingService {
|
||||
|
||||
@Override
|
||||
public String greeting(String name) {
|
||||
return "hello " + name + " from mock greeting";
|
||||
return "hello " + name + " <<<<<<<<<< from mock greeting >>>>>>>>>>";
|
||||
}
|
||||
}
|
||||
----
|
||||
@@ -270,8 +270,7 @@ Now run the tests again (using the command palette as usual), and watch the outp
|
||||
|
||||
[source, none]
|
||||
----
|
||||
[INFO] Running org.acme.people.GreetingServiceTest
|
||||
[INFO] [GreetingServiceTest] (main) greeting: hello Quarkus from mock greeting
|
||||
INFO [GreetingServiceTest] (main) greeting: hello Quarkus <<<<<<<<<< from mock greeting >>>>>>>>>>
|
||||
----
|
||||
|
||||
This confirms that our `MockGreetingService` is being used instead of the original `GreetingService`.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"notBefore" : 0,
|
||||
"revokeRefreshToken" : false,
|
||||
"refreshTokenMaxReuse" : 0,
|
||||
"accessTokenLifespan" : 3000,
|
||||
"accessTokenLifespanForImplicitFlow" : 9000,
|
||||
"accessTokenLifespan" : 300,
|
||||
"accessTokenLifespanForImplicitFlow" : 900,
|
||||
"ssoSessionIdleTimeout" : 1800,
|
||||
"ssoSessionMaxLifespan" : 36000,
|
||||
"ssoSessionIdleTimeoutRememberMe" : 0,
|
||||
@@ -1616,4 +1616,4 @@
|
||||
} ],
|
||||
"keycloakVersion" : "6.0.0",
|
||||
"userManagedAccessAllowed" : false
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,5 @@ spec:
|
||||
version: "1.0"
|
||||
from:
|
||||
kind: DockerImage
|
||||
name: docker.io/schtool/che-quarkus-odo:latest
|
||||
name: docker.io/schtool/che-quarkus-workshop:0.27.0
|
||||
name: "1.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "Quarkus Java, CodeReady, odo",
|
||||
"name": "Quarkus Java CodeReady",
|
||||
"description": "Java JDK Stack for Quarkus Apps",
|
||||
"scope": "general",
|
||||
"workspaceConfig": {
|
||||
@@ -35,7 +35,7 @@
|
||||
"org.eclipse.che.terminal",
|
||||
"org.eclipse.che.ws-agent",
|
||||
"org.eclipse.che.ls.java"
|
||||
|
||||
|
||||
],
|
||||
"attributes": {
|
||||
"memoryLimitBytes": "5368709120"
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} clean package -Pnative -DskipTests",
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} package -Pnative -DskipTests",
|
||||
"name": "Build Native Quarkus App",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
@@ -73,22 +73,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} clean package -DskipTests",
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} package -DskipTests",
|
||||
"name": "Create Executable JAR",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
"goal": "Package",
|
||||
"previewUrl": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "mvn clean compile quarkus:dev -Ddebug -f ${current.project.path}",
|
||||
"name": "Debug Quarkus App",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
"previewUrl": "${server.8080/tcp}",
|
||||
"goal": "Debug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"projects": [],
|
||||
@@ -129,4 +120,3 @@
|
||||
],
|
||||
"id": "quarkus-java"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} clean package -Pnative -DskipTests",
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} package -Pnative -DskipTests",
|
||||
"name": "Build Native Quarkus App",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
@@ -28,22 +28,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} clean package -DskipTests",
|
||||
"commandLine": "MAVEN_OPTS=\"-Xmx1024M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled\" mvn -f ${current.project.path} package -DskipTests",
|
||||
"name": "Create Executable JAR",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
"goal": "Package",
|
||||
"previewUrl": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandLine": "mvn clean compile quarkus:dev -Ddebug -f ${current.project.path}",
|
||||
"name": "Debug Quarkus App",
|
||||
"type": "custom",
|
||||
"attributes": {
|
||||
"goal": "Debug",
|
||||
"previewUrl": "${server.8080/tcp}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"defaultEnv": "default",
|
||||
|
||||
@@ -355,5 +355,5 @@ EOF
|
||||
# RH_PASSWORD=your-password
|
||||
#
|
||||
# then:
|
||||
# DOCKER_BUILDKIT=1 docker build --progress=plain --secret id=rhsm,src=rhsm.secret -t docker.io/username/che-quarkus-odo:latest -f stack.Dockerfile .
|
||||
# docker push docker.io/username/che-quarkus-odo:latest
|
||||
# DOCKER_BUILDKIT=1 docker build --progress=plain --secret id=rhsm,src=rhsm.secret -t docker.io/username/che-quarkus-workshop:latest -f stack.Dockerfile .
|
||||
# docker push docker.io/username/che-quarkus-workshop:0.27.0
|
||||
|
||||
@@ -6,23 +6,23 @@ USER root
|
||||
|
||||
RUN wget -O /tmp/oc.tar.gz https://mirror.openshift.com/pub/openshift-v4/clients/oc/4.1/linux/oc.tar.gz && cd /usr/bin && tar -xvzf /tmp/oc.tar.gz && chmod a+x /usr/bin/oc && rm -f /tmp/oc.tar.gz
|
||||
|
||||
RUN wget -O /tmp/graalvm.tar.gz https://github.com/oracle/graal/releases/download/vm-19.1.1/graalvm-ce-linux-amd64-19.1.1.tar.gz && cd /usr/local && tar -xvzf /tmp/graalvm.tar.gz && rm -rf /tmp/graalvm.tar.gz
|
||||
ENV GRAALVM_HOME="/usr/local/graalvm-ce-19.1.1"
|
||||
RUN ${GRAALVM_HOME}/bin/gu install native-image
|
||||
ENV GRAALVM_VERSION=19.2.1
|
||||
ENV MVN_VERSION=3.6.2
|
||||
ENV GRAALVM_HOME="/usr/local/graalvm-ce-${GRAALVM_VERSION}"
|
||||
ENV MAVEN_OPTS="-Xmx4G -Xss128M -XX:MetaspaceSize=1G -XX:MaxMetaspaceSize=2G -XX:+CMSClassUnloadingEnabled"
|
||||
ENV PATH="/usr/local/maven/apache-maven-${MVN_VERSION}/bin:${PATH}"
|
||||
|
||||
RUN wget -O /tmp/mvn.tar.gz http://www.eu.apache.org/dist/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz
|
||||
RUN wget -O /tmp/graalvm.tar.gz https://github.com/oracle/graal/releases/download/vm-${GRAALVM_VERSION}/graalvm-ce-linux-amd64-${GRAALVM_VERSION}.tar.gz && cd /usr/local && tar -xvzf /tmp/graalvm.tar.gz && rm -rf /tmp/graalvm.tar.gz && ${GRAALVM_HOME}/bin/gu install native-image
|
||||
|
||||
RUN tar xzf /tmp/mvn.tar.gz && rm -rf /tmp/mvn.tar.gz && mkdir /usr/local/maven && mv apache-maven-3.6.0/ /usr/local/maven/ && alternatives --install /usr/bin/mvn mvn /usr/local/maven/apache-maven-3.6.0/bin/mvn 1
|
||||
RUN wget -O /tmp/mvn.tar.gz http://apache.cs.utah.edu/maven/maven-3/${MVN_VERSION}/binaries/apache-maven-${MVN_VERSION}-bin.tar.gz && tar xzf /tmp/mvn.tar.gz && rm -rf /tmp/mvn.tar.gz && mkdir /usr/local/maven && mv apache-maven-${MVN_VERSION}/ /usr/local/maven/ && alternatives --install /usr/bin/mvn mvn /usr/local/maven/apache-maven-${MVN_VERSION}/bin/mvn 1
|
||||
|
||||
RUN --mount=type=secret,id=rhsm username="$(grep RH_USERNAME /run/secrets/rhsm|cut -d= -f2)" && password="$(grep RH_PASSWORD /run/secrets/rhsm|cut -d= -f2)" && subscription-manager register --username $username --password $password --auto-attach && yum install -y gcc zlib-devel && yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && yum install -y siege jq && subscription-manager remove --all && subscription-manager unregister
|
||||
|
||||
USER jboss
|
||||
|
||||
ENV MAVEN_OPTS="-Xmx4G -Xss128M -XX:MetaspaceSize=1G -XX:MaxMetaspaceSize=2G -XX:+CMSClassUnloadingEnabled"
|
||||
ENV PATH="/usr/local/maven/apache-maven-3.6.0/bin:${PATH}"
|
||||
RUN cd /tmp && mkdir project && cd project && mvn io.quarkus:quarkus-maven-plugin:0.27.0:create -DprojectGroupId=org.acme -DprojectArtifactId=footest -Dextensions="quarkus-agroal,quarkus-arc,quarkus-hibernate-orm,quarkus-hibernate-orm-panache,quarkus-jdbc-h2,quarkus-jdbc-postgresql,quarkus-kubernetes,quarkus-scheduler,quarkus-smallrye-fault-tolerance,quarkus-smallrye-health,quarkus-smallrye-opentracing" && mvn -f footest clean compile package && cd / && rm -rf /tmp/project
|
||||
|
||||
RUN cd /tmp && mkdir project && cd project && mvn io.quarkus:quarkus-maven-plugin:0.21.1:create -DprojectGroupId=org.acme -DprojectArtifactId=footest -Dextensions="quarkus-agroal,quarkus-arc,quarkus-hibernate-orm,quarkus-hibernate-orm-panache,quarkus-jdbc-h2,quarkus-jdbc-postgresql,quarkus-kubernetes,quarkus-scheduler,quarkus-smallrye-fault-tolerance,quarkus-smallrye-health,quarkus-smallrye-opentracing" && mvn clean compile package && mvn clean && cd / && rm -rf /tmp/project
|
||||
RUN cd /tmp && mkdir project && cd project && mvn io.quarkus:quarkus-maven-plugin:0.21.1:create -DprojectGroupId=org.acme -DprojectArtifactId=footest -Dextensions="quarkus-smallrye-reactive-streams-operators,quarkus-smallrye-reactive-messaging,quarkus-smallrye-reactive-messaging-kafka,quarkus-swagger-ui,quarkus-vertx,quarkus-kafka-client, quarkus-smallrye-metrics,quarkus-smallrye-openapi" && mvn clean compile package && mvn clean compile package -Pnative && mvn clean && cd / && rm -rf /tmp/project
|
||||
RUN cd /tmp && mkdir project && cd project && mvn io.quarkus:quarkus-maven-plugin:0.27.0:create -DprojectGroupId=org.acme -DprojectArtifactId=footest -Dextensions="quarkus-smallrye-reactive-streams-operators,quarkus-smallrye-reactive-messaging,quarkus-smallrye-reactive-messaging-kafka,quarkus-swagger-ui,quarkus-vertx,quarkus-kafka-client, quarkus-smallrye-metrics,quarkus-smallrye-openapi" && mvn -f footest clean compile package -Pnative && cd / && rm -rf /tmp/project
|
||||
|
||||
RUN siege && sed -i 's/^connection = close/connection = keep-alive/' $HOME/.siege/siege.conf && sed -i 's/^benchmark = false/benchmark = true/' $HOME/.siege/siege.conf
|
||||
|
||||
|
||||
Reference in New Issue
Block a user