├── .editorconfig ├── .github └── workflows │ └── maven.yml ├── .gitignore ├── LICENSE ├── README.md ├── buildProjects.sh ├── cdi ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── decorators │ │ │ ├── Account.java │ │ │ ├── CustomerAccount.java │ │ │ └── LargeWithdrawDecorator.java │ │ │ ├── events │ │ │ ├── BookRequest.java │ │ │ ├── BookRequestListener.java │ │ │ ├── BookRequestPublisher.java │ │ │ └── ForeignBook.java │ │ │ ├── injection │ │ │ ├── BookService.java │ │ │ ├── IsbnValidator.java │ │ │ └── LoggerProducer.java │ │ │ ├── interceptors │ │ │ ├── BookPaymentInterceptor.java │ │ │ ├── BookPaymentProvider.java │ │ │ └── SecurePayment.java │ │ │ └── qualifiers │ │ │ ├── BookDistributor.java │ │ │ ├── BookPlaneDistributor.java │ │ │ ├── BookShipDistributor.java │ │ │ ├── BookStorage.java │ │ │ ├── PlaneDistributor.java │ │ │ └── ShipDistributor.java │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── jax-rs ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de.rieckpil.blog │ │ │ ├── Book.java │ │ │ ├── BookResource.java │ │ │ ├── ClientLoggingResponseFilter.java │ │ │ ├── ContainerLoggingRequestFilter.java │ │ │ ├── GZIPReaderInterceptor.java │ │ │ ├── GZIPWriterInterceptor.java │ │ │ ├── HttpMethodModificationFilter.java │ │ │ ├── JAXRSApplication.java │ │ │ ├── Logged.java │ │ │ ├── QuoteResource.java │ │ │ ├── UserAgentClientFilter.java │ │ │ └── XPoweredByResponseHeaderFilter.java │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── json-b ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── Book.java │ │ │ ├── BookAdapter.java │ │ │ ├── CollectionMapping.java │ │ │ ├── ConfiguredMapping.java │ │ │ ├── CustomMapping.java │ │ │ └── ObjectMapping.java │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── json-p ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── ModifyJson.java │ │ │ ├── ReadingJson.java │ │ │ ├── StreamingJson.java │ │ │ └── WritingJson.java │ ├── resources │ │ └── books.json │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-config ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── BasicConfigurationInjection.java │ │ │ ├── CustomConfigConverter.java │ │ │ ├── CustomConfigObject.java │ │ │ ├── CustomConfigSource.java │ │ │ └── Token.java │ ├── resources │ │ └── META-INF │ │ │ ├── microprofile-config.properties │ │ │ └── services │ │ │ ├── org.eclipse.microprofile.config.spi.ConfigSource │ │ │ └── org.eclipse.microprofile.config.spi.Converter │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-fault-tolerance ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── PlaceHolderApiFallback.java │ │ │ ├── RandomDataPrinter.java │ │ │ └── RandomDataProvider.java │ ├── resources │ │ └── META-INF │ │ │ └── microprofile-config.properties │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-health ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── DiskSizeCheck.java │ │ │ ├── FlakyLivenessCheck.java │ │ │ ├── MultipleHealthCheck.java │ │ │ └── ReadinessCheck.java │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-jwt-auth ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── jwtenizr.jar ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── BookResource.java │ │ │ ├── JAXRSConfiguration.java │ │ │ └── OrderResources.java │ ├── resources │ │ └── META-INF │ │ │ ├── microprofile-config.properties │ │ │ └── publicKey.pem │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-metrics ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── BookCommentClient.java │ │ │ ├── BookRequestProcessor.java │ │ │ ├── BookResource.java │ │ │ └── JAXRSConfiguration.java │ ├── resources │ │ └── META-INF │ │ │ └── microprofile-config.properties │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-open-api ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── Book.java │ │ │ ├── BookResource.java │ │ │ └── JAXRSConfiguration.java │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── microprofile-open-tracing ├── .gitignore ├── README.md ├── book-store-client │ ├── .gitignore │ ├── Dockerfile │ ├── extension │ │ └── lib │ │ │ ├── com.ibm.ws.io.opentracing.zipkintracer-0.31.jar │ │ │ └── features │ │ │ └── opentracingZipkin-0.31.mf │ ├── pom.xml │ ├── server.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── de │ │ │ │ └── rieckpil │ │ │ │ └── blog │ │ │ │ ├── BookProvider.java │ │ │ │ ├── BookResource.java │ │ │ │ ├── JAXRSConfiguration.java │ │ │ │ └── PriceCalculator.java │ │ └── webapp │ │ │ └── WEB-INF │ │ │ ├── beans.xml │ │ │ ├── ibm-web-ext.xml │ │ │ └── payara-web.xml │ │ └── test │ │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ └── ApplicationIT.java │ │ └── resources │ │ └── log4j.properties ├── book-store │ ├── .gitignore │ ├── Dockerfile │ ├── extension │ │ └── lib │ │ │ ├── com.ibm.ws.io.opentracing.zipkintracer-0.31.jar │ │ │ └── features │ │ │ └── opentracingZipkin-0.31.mf │ ├── pom.xml │ ├── server.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── de │ │ │ │ └── rieckpil │ │ │ │ └── blog │ │ │ │ ├── BookResource.java │ │ │ │ ├── ContainerLoggingRequestFilter.java │ │ │ │ ├── JAXRSConfiguration.java │ │ │ │ └── PriceResource.java │ │ └── webapp │ │ │ └── WEB-INF │ │ │ ├── beans.xml │ │ │ ├── ibm-web-ext.xml │ │ │ └── payara-web.xml │ │ └── test │ │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ └── ApplicationIT.java │ │ └── resources │ │ └── log4j.properties ├── buildAndRun.bat ├── buildAndRun.sh └── docker-compose.yml ├── microprofile-rest-client ├── .gitignore ├── Dockerfile ├── README.md ├── buildAndRun.bat ├── buildAndRun.sh ├── pom.xml └── src │ ├── main │ ├── java │ │ └── de │ │ │ └── rieckpil │ │ │ └── blog │ │ │ ├── GlobalClientHeaders.java │ │ │ ├── JSONPlaceholderClient.java │ │ │ ├── PostService.java │ │ │ └── ResponseLoggingFilter.java │ ├── resources │ │ └── META-INF │ │ │ └── microprofile-config.properties │ └── webapp │ │ └── WEB-INF │ │ ├── beans.xml │ │ ├── ibm-web-ext.xml │ │ └── payara-web.xml │ └── test │ ├── java │ └── de │ │ └── rieckpil │ │ └── blog │ │ └── ApplicationIT.java │ └── resources │ └── log4j.properties ├── pom.xml └── server.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | tab_width = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Build Java 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - uses: actions/cache@v1 9 | with: 10 | path: ~/.m2/repository 11 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 12 | restore-keys: | 13 | ${{ runner.os }}-maven- 14 | - name: Set up JDK 11 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 11 18 | - name: Build projects 19 | run: ./buildProjects.sh 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .project 3 | .settings/ 4 | .idea 5 | *.iml 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Philip Riecks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting started with MicroProfile 2 | 3 | [![](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/workflows/Build%20Java/badge.svg)](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/actions) 4 | 5 |

6 | 7 |  Bundle Logo 8 | 9 |

10 | 11 | » Repository for the Getting Started with MicroProfile [Course Bundle](https://rieckpil.de/p/eclipse-microprofile-course-bundle). 12 | 13 | ## Specifications: 14 | 15 | * **CDI**: [YouTube Video](https://youtu.be/Q8jHRDu9Fbo) - [Blog post](https://rieckpil.de/whatis-contexts-and-dependency-injection-cdi/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/cdi) 16 | * **JAX-RS**: [YouTube Video](https://youtu.be/-TmKXm0k7UI) - [Blog post](https://rieckpil.de/whatis-jakarta-restful-web-services-jax-rs/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/jax-rs) 17 | * **JSON-B**: [YouTube Video](https://youtu.be/3TbbivV2Epk) - [Blog post](https://rieckpil.de/whatis-json-binding-json-b/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/json-b) 18 | * **JSON-P**: [YouTube Video](https://youtu.be/2H7z_MbWwDQ) - [Blog post](https://rieckpil.de/whatis-json-processing-json-p/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/json-p) 19 | * **MicroProfile Config**: [YouTube Video](https://youtu.be/0h3QceSBBiY) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-config/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-config) 20 | * **MicroProfile Fault Tolerance**: [YouTube Video](https://www.youtube.com/watch?v=_O4EjWHF0TQ) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-fault-tolerance/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-fault-tolerance) 21 | * **MicroProfile Metrics**: [YouTube Video](https://www.youtube.com/watch?v=jI6DoNYVd-U) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-metrics/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-metrics) 22 | * **MicroProfile JWT Auth**: [YouTube Video](https://youtu.be/8O3D2tNx1uM) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-jwt-auth/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-jwt-auth) 23 | * **MicroProfile Rest Client**: [YouTube Video](https://youtu.be/HJWxI_T3FKo) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-rest-client/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-rest-client) 24 | * **MicroProfile OpenAPI**: [YouTube Video](https://www.youtube.com/watch?v=Rn7T26UW_H8) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-openapi/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-open-api) 25 | * **MicroProfile OpenTracing**: [YouTube Video](https://www.youtube.com/watch?v=b43XgElBxEo) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-opentracing/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-open-tracing) 26 | * **MicroProfile Health**: [YouTube Video](https://www.youtube.com/watch?v=nq_gdPUTx5c) - [Blog post](https://rieckpil.de/whatis-eclipse-microprofile-health/) - [Source code](https://github.com/rieckpil/getting-started-with-eclipse-microprofile/tree/master/microprofile-health) 27 | 28 | ## Technologies used for this series: 29 | 30 | * **MicroProfile 3.3** 31 | * **OpenLiberty 20.0.0.5** 32 | * **Java 11** 33 | * **Maven 3.6** 34 | * **WAD** (Watch and Deploy) from [Adam Bien](https://wad.sh/) ([setup](https://rieckpil.de/review-improved-java-jakarta-ee-productivity-with-adam-biens-wad-watch-and-deploy/)) 35 | * **JWTENIZR** from [Adam Bien](http://jwtenizr.sh/) 36 | 37 | ## Start the example applications 38 | 39 | Each subfolder contains a `buildAndRun.sh` (Linux/Mac) and `buildAndRun.bat` (Windows) file to build and start the application on your machine using **Docker**. You just need **Java 11** and **Maven** installed and a running Docker daemon to start everything. Once the application is up- and running, you can visit http://localhost:9080 to access it (if any JAX-RS endpoint is available in the project). 40 | 41 | ## Open Liberty configuration 42 | 43 | All projects use the following base `server.xml` configuration: 44 | 45 | ```xml 46 | 47 | 48 | 49 | 50 | microProfile-3.3 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | The following `ibm-web-ext.xml` is used within the project to deploy the application to the root path `/`: 63 | 64 | ```xml 65 | 70 | 71 | 72 | ``` 73 | -------------------------------------------------------------------------------- /buildProjects.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | declare -a arr=( 7 | "cdi" 8 | "jax-rs" 9 | "json-b" 10 | "json-p" 11 | "microprofile-config" 12 | "microprofile-fault-tolerance" 13 | "microprofile-health" 14 | "microprofile-jwt-auth" 15 | "microprofile-metrics" 16 | "microprofile-open-api" 17 | "microprofile-open-tracing/book-store" 18 | "microprofile-open-tracing/book-store-client" 19 | "microprofile-rest-client" 20 | ) 21 | 22 | for project in "${arr[@]}" 23 | do 24 | mvn -B -f $project/pom.xml clean verify 25 | done 26 | -------------------------------------------------------------------------------- /cdi/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /cdi/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/cdi.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /cdi/README.md: -------------------------------------------------------------------------------- 1 | # CDI (Contexts and Dependency Injection) 2 | 3 | * [GitHub](https://github.com/eclipse-ee4j/cdi) 4 | * [Spec](http://www.cdi-spec.org/) 5 | * Current version: **2.0** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: Contexts and Dependency Injection (CDI)](https://rieckpil.de/whatis-contexts-and-dependency-injection-cdi/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - CDI 2.0](https://youtu.be/Q8jHRDu9Fbo) -------------------------------------------------------------------------------- /cdi/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/cdi . 4 | call docker rm -f cdi 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name cdi de.rieckpil.blog/cdi -------------------------------------------------------------------------------- /cdi/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/cdi . 3 | docker rm -f cdi || true && docker run -d -p 9080:9080 -p 9443:9443 --name cdi de.rieckpil.blog/cdi -------------------------------------------------------------------------------- /cdi/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | cdi 14 | 1.0-SNAPSHOT 15 | war 16 | 17 | 18 | ${project.artifactId} 19 | 20 | 21 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/decorators/Account.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.decorators; 2 | 3 | public interface Account { 4 | 5 | Double getBalance(); 6 | 7 | void withdrawMoney(Double amount); 8 | } 9 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/decorators/CustomerAccount.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.decorators; 2 | 3 | public class CustomerAccount implements Account { 4 | 5 | @Override 6 | public Double getBalance() { 7 | return 42.0; 8 | } 9 | 10 | @Override 11 | public void withdrawMoney(Double amount) { 12 | System.out.println("Withdraw money from customer: " + amount); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/decorators/LargeWithdrawDecorator.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.decorators; 2 | 3 | import javax.decorator.Decorator; 4 | import javax.decorator.Delegate; 5 | import javax.inject.Inject; 6 | 7 | @Decorator 8 | // @Priority(100) 9 | public abstract class LargeWithdrawDecorator implements Account { 10 | 11 | @Inject 12 | @Delegate 13 | private Account account; 14 | 15 | @Override 16 | public void withdrawMoney(Double amount) { 17 | if (amount >= 100.0) { 18 | System.out.println("A large amount of money gets withdrawn!!!"); 19 | // e.g. do further checks 20 | } 21 | account.withdrawMoney(amount); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/events/BookRequest.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.events; 2 | 3 | public class BookRequest { 4 | 5 | private String bookName; 6 | private Integer customerId; 7 | 8 | public BookRequest() { 9 | } 10 | 11 | public BookRequest(String bookName, Integer customerId) { 12 | this.bookName = bookName; 13 | this.customerId = customerId; 14 | } 15 | 16 | public String getBookName() { 17 | return bookName; 18 | } 19 | 20 | public void setBookName(String bookName) { 21 | this.bookName = bookName; 22 | } 23 | 24 | public Integer getCustomerId() { 25 | return customerId; 26 | } 27 | 28 | public void setCustomerId(Integer customerId) { 29 | this.customerId = customerId; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "BookRequest{" + 35 | "bookName='" + bookName + '\'' + 36 | ", customerId=" + customerId + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/events/BookRequestListener.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.events; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.event.Observes; 5 | import javax.enterprise.event.ObservesAsync; 6 | import javax.enterprise.event.TransactionPhase; 7 | import javax.enterprise.inject.Default; 8 | 9 | @ApplicationScoped 10 | public class BookRequestListener { 11 | 12 | public void onBookRequest(@Observes(during = TransactionPhase.AFTER_SUCCESS) @Default BookRequest bookRequest) { 13 | System.out.println("New book request incoming: " + bookRequest.toString()); 14 | } 15 | 16 | public void onBookForeignRequest(@Observes @ForeignBook BookRequest bookRequest) { 17 | System.out.println("New foreign book request incoming: " + bookRequest.toString()); 18 | } 19 | 20 | public void onBookRequestAsync(@ObservesAsync BookRequest bookRequest) { 21 | System.out.println("New book request incoming async: " + bookRequest.toString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/events/BookRequestPublisher.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.events; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Event; 6 | import javax.enterprise.event.Observes; 7 | import javax.inject.Inject; 8 | 9 | @ApplicationScoped 10 | public class BookRequestPublisher { 11 | 12 | @Inject 13 | private Event bookRequestEvent; 14 | 15 | @Inject 16 | @ForeignBook 17 | private Event foreignBookRequestEvent; 18 | 19 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 20 | 21 | this.bookRequestEvent.fire(new BookRequest("MicroProfile 3.0", 1)); 22 | this.foreignBookRequestEvent.fire(new BookRequest("MicroProfile 3.0 (German)", 1)); 23 | 24 | this.bookRequestEvent 25 | .fireAsync(new BookRequest("MicroProfile 3.0", 1)) 26 | .handle((request, error) -> { 27 | if (error == null) { 28 | System.out.println("Successfully fired async event"); 29 | return request; 30 | } else { 31 | System.out.println("Error occured during async event"); 32 | return null; 33 | } 34 | }) 35 | .thenAccept(r -> System.out.println(r)); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/events/ForeignBook.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.events; 2 | 3 | import javax.inject.Qualifier; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.*; 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | @Qualifier 11 | @Retention(RUNTIME) 12 | @Target({TYPE, METHOD, FIELD, PARAMETER}) 13 | public @interface ForeignBook { 14 | } 15 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/injection/BookService.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.injection; 2 | 3 | import de.rieckpil.blog.decorators.CustomerAccount; 4 | import de.rieckpil.blog.interceptors.BookPaymentProvider; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.enterprise.context.Initialized; 8 | import javax.enterprise.event.Observes; 9 | import javax.inject.Inject; 10 | import java.util.logging.Logger; 11 | 12 | @ApplicationScoped 13 | public class BookService { 14 | 15 | @Inject 16 | private Logger logger; 17 | 18 | @Inject 19 | private IsbnValidator isbnValidator; 20 | 21 | @Inject 22 | private BookPaymentProvider paymentProvider; 23 | 24 | @Inject 25 | private CustomerAccount customerAccount; 26 | 27 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 28 | this.paymentProvider.withdrawMoneyFromCustomer("Duke", 123); 29 | this.paymentProvider.withdrawMoneyFromCustomer("Mike", 123); 30 | 31 | this.storeBook("Java 11", "123-3-42-133713-4"); 32 | this.storeBook("Java 42", "123-3-42-133713-"); 33 | 34 | this.customerAccount.withdrawMoney(42.0); 35 | this.customerAccount.withdrawMoney(142.0); 36 | } 37 | 38 | public void storeBook(String bookName, String isbn) { 39 | if (isbnValidator.validateIsbn(isbn)) { 40 | logger.info("Store book with name: " + bookName); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/injection/IsbnValidator.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.injection; 2 | 3 | import javax.inject.Inject; 4 | import java.util.logging.Logger; 5 | 6 | public class IsbnValidator { 7 | 8 | @Inject 9 | private Logger logger; 10 | 11 | public boolean validateIsbn(String isbn) { 12 | if (isbn.replace("-", "").length() < 13) { 13 | logger.warning("ISBN validation failed for ISBN: " + isbn); 14 | return false; 15 | } 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/injection/LoggerProducer.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.injection; 2 | 3 | import javax.enterprise.inject.Produces; 4 | import javax.enterprise.inject.spi.InjectionPoint; 5 | import java.util.logging.Logger; 6 | 7 | public class LoggerProducer { 8 | 9 | @Produces 10 | public Logger produceLogger(InjectionPoint injectionPoint) { 11 | return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/interceptors/BookPaymentInterceptor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.interceptors; 2 | 3 | import javax.interceptor.AroundInvoke; 4 | import javax.interceptor.Interceptor; 5 | import javax.interceptor.InvocationContext; 6 | 7 | @Interceptor 8 | @SecurePayment 9 | public class BookPaymentInterceptor { 10 | 11 | @AroundInvoke 12 | public Object secureBookPayment(InvocationContext ctx) throws Exception { 13 | System.out.println("--- Intercepting: " + ctx.getMethod().getName()); 14 | 15 | if (((String) ctx.getParameters()[0]).equalsIgnoreCase("Duke")) { 16 | ctx.setParameters(new Object[]{"Duke", 999}); 17 | } 18 | 19 | return ctx.proceed(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/interceptors/BookPaymentProvider.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.interceptors; 2 | 3 | @SecurePayment 4 | public class BookPaymentProvider { 5 | 6 | public void withdrawMoneyFromCustomer(String customerName, Integer amount) { 7 | System.out.println("Withdraw money from: " + customerName + " amount: " + amount); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/interceptors/SecurePayment.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.interceptors; 2 | 3 | import javax.interceptor.InterceptorBinding; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.METHOD; 8 | import static java.lang.annotation.ElementType.TYPE; 9 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 10 | 11 | @InterceptorBinding 12 | @Target({METHOD, TYPE}) 13 | @Retention(RUNTIME) 14 | public @interface SecurePayment { 15 | } 16 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/BookDistributor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | public interface BookDistributor { 4 | 5 | void distributeBook(String bookName); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/BookPlaneDistributor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | @PlaneDistributor 4 | public class BookPlaneDistributor implements BookDistributor { 5 | 6 | @Override 7 | public void distributeBook(String bookName) { 8 | System.out.println("Distributing book by plane"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/BookShipDistributor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | @ShipDistributor 4 | public class BookShipDistributor implements BookDistributor { 5 | 6 | @Override 7 | public void distributeBook(String bookName) { 8 | System.out.println("Distributing book by ship"); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/BookStorage.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.annotation.PreDestroy; 5 | import javax.enterprise.context.ApplicationScoped; 6 | import javax.enterprise.inject.Instance; 7 | import javax.inject.Inject; 8 | 9 | @ApplicationScoped 10 | public class BookStorage { 11 | 12 | @Inject 13 | private Instance bookDistributors; 14 | 15 | @Inject 16 | @PlaneDistributor 17 | private BookDistributor bookPlaneDistributor; 18 | 19 | @Inject 20 | @ShipDistributor 21 | private BookDistributor bookShipDistributor; 22 | 23 | @PostConstruct 24 | public void init() { 25 | this.distributeBookToCustomer("Java for Beginners"); 26 | } 27 | 28 | @PreDestroy 29 | public void onPreDestroy() { 30 | System.out.println("Bean is going to be destroyed"); 31 | } 32 | 33 | public void distributeBookToCustomer(String bookName) { 34 | bookDistributors.forEach(b -> b.distributeBook(bookName)); 35 | bookPlaneDistributor.distributeBook(bookName); 36 | bookShipDistributor.distributeBook(bookName); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/PlaneDistributor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | import javax.inject.Qualifier; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.*; 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | @Qualifier 11 | @Retention(RUNTIME) 12 | @Target({TYPE, METHOD, FIELD, PARAMETER}) 13 | public @interface PlaneDistributor { 14 | } 15 | -------------------------------------------------------------------------------- /cdi/src/main/java/de/rieckpil/blog/qualifiers/ShipDistributor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog.qualifiers; 2 | 3 | import javax.inject.Qualifier; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.*; 8 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 9 | 10 | @Qualifier 11 | @Retention(RUNTIME) 12 | @Target({TYPE, METHOD, FIELD, PARAMETER}) 13 | public @interface ShipDistributor { 14 | } 15 | -------------------------------------------------------------------------------- /cdi/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | de.rieckpil.blog.interceptors.BookPaymentInterceptor 8 | 9 | 10 | de.rieckpil.blog.decorators.LargeWithdrawDecorator 11 | 12 | 13 | -------------------------------------------------------------------------------- /cdi/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /cdi/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /cdi/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cdi/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /jax-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /jax-rs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/jax-rs.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /jax-rs/README.md: -------------------------------------------------------------------------------- 1 | # JAX-RS: Jakarta RESTful Web Services 2 | 3 | * [GitHub](https://github.com/eclipse-ee4j/jaxrs-api) 4 | * [Spec homepage](https://projects.eclipse.org/projects/ee4j.jaxrs) 5 | * Current version: **2.1** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: Jakarta RESTful Web Services (JAX-RS)](https://rieckpil.de/whatis-jakarta-restful-web-services-jax-rs/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - JAX-RS 2.1](https://youtu.be/-TmKXm0k7UI) 8 | 9 | ## Request & response flow 10 | 11 | ``` 12 | CLIENT SERVER 13 | +----------------------------------------------------------------+ +-----------------------------------------------------------------------------------+ 14 | | | | | 15 | | ClientRequestFilter -> WriterInterceptor -> MessageBodyWriter ----------> PreMatchingRequestFilter -> ContainerRequestFilter ---> ReaderInterceptor | 16 | | | | | | 17 | | | | v | 18 | | | | MessageBodyReader | 19 | | | | | | 20 | | | | v | 21 | | | | ResourceMethod | 22 | | | | | | 23 | | | | v | 24 | | MessageBodyReader <- ReaderInterceptor <- ClientResponseFilter <----------- MessageBodyWriter <--- WriterInterceptor <--- ContainerResponseFilter | 25 | | | | | 26 | +----------------------------------------------------------------+ +-----------------------------------------------------------------------------------+ 27 | ``` -------------------------------------------------------------------------------- /jax-rs/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/jax-rs . 4 | call docker rm -f jax-rs 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name jax-rs de.rieckpil.blog/jax-rs -------------------------------------------------------------------------------- /jax-rs/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/jax-rs . 3 | docker rm -f jax-rs || true && docker run -d -p 9080:9080 -p 9443:9443 --name jax-rs de.rieckpil.blog/jax-rs -------------------------------------------------------------------------------- /jax-rs/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | jax-rs 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/Book.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | public class Book { 4 | 5 | private Long id; 6 | private String title; 7 | private String author; 8 | 9 | public Book() { 10 | 11 | } 12 | 13 | public Book(Long id, String title, String author) { 14 | this.id = id; 15 | this.title = title; 16 | this.author = author; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getTitle() { 28 | return title; 29 | } 30 | 31 | public void setTitle(String title) { 32 | this.title = title; 33 | } 34 | 35 | public String getAuthor() { 36 | return author; 37 | } 38 | 39 | public void setAuthor(String author) { 40 | this.author = author; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.ws.rs.*; 5 | import javax.ws.rs.container.AsyncResponse; 6 | import javax.ws.rs.container.Suspended; 7 | import javax.ws.rs.core.*; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | @Path("books") 12 | @Produces(MediaType.APPLICATION_JSON) 13 | @Consumes(MediaType.APPLICATION_JSON) 14 | public class BookResource { 15 | 16 | private List bookStore; 17 | 18 | @PostConstruct 19 | public void init() { 20 | this.bookStore = new ArrayList<>(); 21 | this.bookStore.add(new Book(1L, "Java 11", "Duke")); 22 | this.bookStore.add(new Book(2L, "Java 12", "Duke")); 23 | this.bookStore.add(new Book(3L, "Java 13", "Duke")); 24 | } 25 | 26 | @GET 27 | @Logged 28 | public Response getBooks(@HeaderParam("User-Agent") String userAgent) { 29 | System.out.println(userAgent); 30 | return Response.ok(this.bookStore).build(); 31 | } 32 | 33 | @GET 34 | @Path("async") 35 | public void getBooksAsync(@Suspended final AsyncResponse asyncResponse) { 36 | // do long-running task with e.g. @Asynchronous annotation of MicroProfile Fault Tolerance or from EJB 37 | asyncResponse.resume(this.bookStore); 38 | } 39 | 40 | @GET 41 | @Path("/{id}") 42 | public Response getBookById(@PathParam("id") Long id, @QueryParam("title") @DefaultValue("") String title) { 43 | Book requestedBook = this.bookStore 44 | .stream() 45 | .filter(b -> b.getId().equals(id)) 46 | .findFirst().orElse(null); 47 | 48 | if (requestedBook == null) { 49 | return Response.status(Response.Status.NOT_FOUND).build(); 50 | } 51 | 52 | return Response.ok(requestedBook).build(); 53 | } 54 | 55 | @POST 56 | public Response createNewBook(Book bookToStore, @Context UriInfo uriInfo) { 57 | this.bookStore.add(bookToStore); 58 | UriBuilder builder = uriInfo.getAbsolutePathBuilder(); 59 | builder.path(bookToStore.getId().toString()); 60 | return Response.created(builder.build()).build(); 61 | } 62 | 63 | @DELETE 64 | @Path("/{id}") 65 | public Response deleteBook(@PathParam("id") Long id, @HeaderParam("User-Agent") String userAgent) { 66 | this.bookStore.remove(id); 67 | return Response.noContent().build(); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/ClientLoggingResponseFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.client.ClientRequestContext; 4 | import javax.ws.rs.client.ClientResponseContext; 5 | import javax.ws.rs.client.ClientResponseFilter; 6 | import javax.ws.rs.ext.Provider; 7 | import java.io.IOException; 8 | 9 | @Provider 10 | public class ClientLoggingResponseFilter implements ClientResponseFilter { 11 | 12 | @Override 13 | public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { 14 | System.out.println("Response filter for JAX-RS Client"); 15 | responseContext.getHeaders().forEach((k, v) -> System.out.println(k + ":" + v)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/ContainerLoggingRequestFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.container.ContainerRequestContext; 4 | import javax.ws.rs.container.ContainerRequestFilter; 5 | import javax.ws.rs.ext.Provider; 6 | import java.time.LocalDateTime; 7 | 8 | @Provider 9 | @Logged 10 | public class ContainerLoggingRequestFilter implements ContainerRequestFilter { 11 | 12 | @Override 13 | public void filter(ContainerRequestContext requestContext) { 14 | System.out.println("=============="); 15 | System.out.println("Incoming request at: " + LocalDateTime.now()); 16 | requestContext.getHeaders().forEach((k, v) -> System.out.println(k + ":" + v)); 17 | System.out.println("=============="); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/GZIPReaderInterceptor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.WebApplicationException; 4 | import javax.ws.rs.ext.ReaderInterceptor; 5 | import javax.ws.rs.ext.ReaderInterceptorContext; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.zip.GZIPInputStream; 9 | 10 | public class GZIPReaderInterceptor implements ReaderInterceptor { 11 | 12 | @Override 13 | public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { 14 | final InputStream originalInputStream = context.getInputStream(); 15 | context.setInputStream(new GZIPInputStream(originalInputStream)); 16 | return context.proceed(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/GZIPWriterInterceptor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.WebApplicationException; 4 | import javax.ws.rs.ext.WriterInterceptor; 5 | import javax.ws.rs.ext.WriterInterceptorContext; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.util.zip.GZIPOutputStream; 9 | 10 | public class GZIPWriterInterceptor implements WriterInterceptor { 11 | 12 | @Override 13 | public void aroundWriteTo(WriterInterceptorContext context) 14 | throws IOException, WebApplicationException { 15 | 16 | final OutputStream outputStream = context.getOutputStream(); 17 | context.setOutputStream(new GZIPOutputStream(outputStream)); 18 | context.proceed(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/HttpMethodModificationFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.container.ContainerRequestContext; 4 | import javax.ws.rs.container.ContainerRequestFilter; 5 | import java.io.IOException; 6 | 7 | // uncomment to activate this evil filter ;) 8 | // @Provider 9 | public class HttpMethodModificationFilter implements ContainerRequestFilter { 10 | 11 | @Override 12 | public void filter(ContainerRequestContext requestContext) throws IOException { 13 | 14 | if (requestContext.getMethod().equalsIgnoreCase("DELETE")) { 15 | requestContext.setMethod("GET"); 16 | } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/JAXRSApplication.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSApplication extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/Logged.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.NameBinding; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | @NameBinding 8 | @Retention(RetentionPolicy.RUNTIME) 9 | public @interface Logged { 10 | } 11 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/QuoteResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.annotation.PreDestroy; 5 | import javax.json.JsonObject; 6 | import javax.ws.rs.*; 7 | import javax.ws.rs.client.Client; 8 | import javax.ws.rs.client.ClientBuilder; 9 | import javax.ws.rs.client.WebTarget; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | import java.util.concurrent.CompletionStage; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | @Path("quotes") 16 | public class QuoteResource { 17 | 18 | private WebTarget quotesApiTarget; 19 | private Client client; 20 | 21 | @PostConstruct 22 | public void initClient() { 23 | 24 | ClientBuilder clientBuilder = ClientBuilder.newBuilder() 25 | .connectTimeout(5, TimeUnit.SECONDS) 26 | .readTimeout(5, TimeUnit.SECONDS) 27 | .register(UserAgentClientFilter.class) 28 | .register(ClientLoggingResponseFilter.class); 29 | 30 | this.client = clientBuilder.build(); 31 | this.quotesApiTarget = client.target("https://quotes.rest").path("qod"); 32 | } 33 | 34 | @PreDestroy 35 | public void tearDown() { 36 | this.client.close(); 37 | } 38 | 39 | @GET 40 | @Produces(MediaType.TEXT_PLAIN) 41 | public Response getQuote() { 42 | 43 | JsonObject quoteApiResult = this.quotesApiTarget 44 | .request() 45 | .header("X-Foo", "bar") 46 | .accept(MediaType.APPLICATION_JSON) 47 | .get() 48 | .readEntity(JsonObject.class); 49 | 50 | CompletionStage rxQuoteApiResult = this.quotesApiTarget 51 | .request() 52 | .header("X-Foo", "bar") 53 | .accept(MediaType.APPLICATION_JSON) 54 | .rx() 55 | .get(JsonObject.class); 56 | 57 | String quote = quoteApiResult 58 | .get("contents").asJsonObject() 59 | .get("quotes").asJsonArray().get(0).asJsonObject() 60 | .get("quote").toString(); 61 | 62 | return Response.ok(quote).build(); 63 | } 64 | 65 | @POST 66 | @Consumes(MediaType.TEXT_PLAIN) 67 | public Response createQuote(String text) { 68 | System.out.println(text); 69 | return Response.created(null).build(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/UserAgentClientFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.client.ClientRequestContext; 4 | import javax.ws.rs.client.ClientRequestFilter; 5 | import javax.ws.rs.ext.Provider; 6 | import java.io.IOException; 7 | 8 | @Provider 9 | public class UserAgentClientFilter implements ClientRequestFilter { 10 | 11 | @Override 12 | public void filter(ClientRequestContext requestContext) throws IOException { 13 | requestContext.getHeaders().add("User-Agent", "MP-Service-XYZ"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /jax-rs/src/main/java/de.rieckpil.blog/XPoweredByResponseHeaderFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.Priority; 4 | import javax.ws.rs.container.ContainerRequestContext; 5 | import javax.ws.rs.container.ContainerResponseContext; 6 | import javax.ws.rs.container.ContainerResponseFilter; 7 | import javax.ws.rs.ext.Provider; 8 | import java.io.IOException; 9 | 10 | @Priority(100) 11 | @Provider 12 | public class XPoweredByResponseHeaderFilter implements ContainerResponseFilter { 13 | 14 | @Override 15 | public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { 16 | responseContext.getHeaders().add("X-Powered-By", "MicroProfile"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /jax-rs/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /jax-rs/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /jax-rs/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /jax-rs/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /jax-rs/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /json-b/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /json-b/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/json-b.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /json-b/README.md: -------------------------------------------------------------------------------- 1 | # JSON-B: Java API for JSON Binding 2 | 3 | * [GitHub](https://github.com/eclipse-ee4j/jsonb-api) 4 | * [Spec](https://javaee.github.io/jsonb-spec/index.html) 5 | * Current version: **1.0** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: JSON Binding (JSON-B)](https://rieckpil.de/whatis-json-binding-json-b/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - JSON-B 1.0](https://youtu.be/3TbbivV2Epk) 8 | -------------------------------------------------------------------------------- /json-b/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/json-b . 4 | call docker rm -f json-b 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name json-b de.rieckpil.blog/json-b -------------------------------------------------------------------------------- /json-b/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/json-b . 3 | docker rm -f json-b || true && docker run -d -p 9080:9080 -p 9443:9443 --name json-b de.rieckpil.blog/json-b -------------------------------------------------------------------------------- /json-b/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | json-b 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/Book.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.json.bind.annotation.JsonbDateFormat; 4 | import javax.json.bind.annotation.JsonbNumberFormat; 5 | import javax.json.bind.annotation.JsonbProperty; 6 | import javax.json.bind.annotation.JsonbTransient; 7 | import java.math.BigDecimal; 8 | import java.time.LocalDate; 9 | 10 | // @JsonbNillable 11 | public class Book { 12 | 13 | @JsonbProperty(nillable = true) 14 | private String title; 15 | 16 | @JsonbDateFormat("dd.MM.yyyy") 17 | private LocalDate creationDate; 18 | private long pages; 19 | 20 | @JsonbTransient 21 | private boolean isPublished; 22 | private String author; 23 | 24 | @JsonbNumberFormat("#0.00") 25 | private BigDecimal price; 26 | 27 | public Book() { 28 | } 29 | 30 | // @JsonbCreator 31 | public Book(@JsonbProperty("book-title") String title) { 32 | this.title = title; 33 | } 34 | 35 | public Book(String title, LocalDate creationDate, long pages, boolean isPublished, String author, BigDecimal price) { 36 | this.title = title; 37 | this.creationDate = creationDate; 38 | this.pages = pages; 39 | this.isPublished = isPublished; 40 | this.author = author; 41 | this.price = price; 42 | } 43 | 44 | public String getTitle() { 45 | return title; 46 | } 47 | 48 | public void setTitle(String title) { 49 | this.title = title; 50 | } 51 | 52 | public LocalDate getCreationDate() { 53 | return creationDate; 54 | } 55 | 56 | public void setCreationDate(LocalDate creationDate) { 57 | this.creationDate = creationDate; 58 | } 59 | 60 | public long getPages() { 61 | return pages; 62 | } 63 | 64 | public void setPages(long pages) { 65 | this.pages = pages; 66 | } 67 | 68 | public boolean isPublished() { 69 | return isPublished; 70 | } 71 | 72 | public void setPublished(boolean published) { 73 | isPublished = published; 74 | } 75 | 76 | public String getAuthor() { 77 | return author; 78 | } 79 | 80 | public void setAuthor(String author) { 81 | this.author = author; 82 | } 83 | 84 | public BigDecimal getPrice() { 85 | return price; 86 | } 87 | 88 | public void setPrice(BigDecimal price) { 89 | this.price = price; 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return "Book{" + 95 | "title='" + title + '\'' + 96 | ", creationDate=" + creationDate + 97 | ", pages=" + pages + 98 | ", isPublished=" + isPublished + 99 | ", author='" + author + '\'' + 100 | ", price=" + price + 101 | '}'; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/BookAdapter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.json.Json; 4 | import javax.json.JsonObject; 5 | import javax.json.bind.adapter.JsonbAdapter; 6 | import java.math.BigDecimal; 7 | import java.time.LocalDate; 8 | 9 | public class BookAdapter implements JsonbAdapter { 10 | 11 | @Override 12 | public JsonObject adaptToJson(Book book) throws Exception { 13 | return Json.createObjectBuilder() 14 | .add("title", book.getTitle() + " - " + book.getAuthor()) 15 | .add("creationDate", book.getCreationDate().toEpochDay()) 16 | .add("pages", book.getPages()) 17 | .add("price", book.getPrice().multiply(BigDecimal.valueOf(2l))) 18 | .build(); 19 | } 20 | 21 | @Override 22 | public Book adaptFromJson(JsonObject jsonObject) throws Exception { 23 | Book book = new Book(); 24 | book.setTitle(jsonObject.getString("title").split("-")[0].trim()); 25 | book.setAuthor(jsonObject.getString("title").split("-")[1].trim()); 26 | book.setPages(jsonObject.getInt("pages")); 27 | book.setPublished(false); 28 | book.setPrice(BigDecimal.valueOf(jsonObject.getJsonNumber("price").longValue())); 29 | book.setCreationDate(LocalDate.ofEpochDay(jsonObject.getInt("creationDate"))); 30 | return book; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/CollectionMapping.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.bind.Jsonb; 7 | import javax.json.bind.JsonbBuilder; 8 | import java.math.BigDecimal; 9 | import java.time.LocalDate; 10 | import java.time.temporal.ChronoUnit; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @ApplicationScoped 15 | public class CollectionMapping { 16 | 17 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 18 | List bookList = new ArrayList<>(); 19 | bookList.add(new Book("Java 11", LocalDate.now(), 100, true, "Duke", new BigDecimal(39.95))); 20 | bookList.add(new Book("Java 15", LocalDate.now().plus(365, ChronoUnit.DAYS), 110, false, "Duke", new BigDecimal(50.002))); 21 | 22 | Jsonb jsonb = JsonbBuilder.create(); 23 | 24 | String result = jsonb.toJson(bookList); 25 | System.out.println(result); 26 | 27 | List serializedBookList = jsonb 28 | .fromJson(result, new ArrayList() { 29 | }.getClass().getGenericSuperclass()); 30 | 31 | serializedBookList.forEach(b -> System.out.println(b.toString())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/ConfiguredMapping.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.bind.Jsonb; 7 | import javax.json.bind.JsonbBuilder; 8 | import javax.json.bind.JsonbConfig; 9 | import javax.json.bind.config.PropertyNamingStrategy; 10 | import javax.json.bind.config.PropertyOrderStrategy; 11 | import java.math.BigDecimal; 12 | import java.time.LocalDate; 13 | import java.util.Locale; 14 | 15 | @ApplicationScoped 16 | public class ConfiguredMapping { 17 | 18 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 19 | 20 | Book book = new Book("Java 11", LocalDate.now(), 1, false, null, new BigDecimal(50.50)); 21 | 22 | JsonbConfig config = new JsonbConfig() 23 | .withNullValues(false) 24 | .withFormatting(true) 25 | .withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL) 26 | .withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES) 27 | .withDateFormat("dd-MM-YYYY", Locale.GERMAN); 28 | 29 | Jsonb jsonb = JsonbBuilder.create(config); 30 | 31 | String jsonString = jsonb.toJson(book); 32 | System.out.println(jsonString); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/CustomMapping.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.bind.Jsonb; 7 | import javax.json.bind.JsonbBuilder; 8 | import javax.json.bind.JsonbConfig; 9 | import java.math.BigDecimal; 10 | import java.time.LocalDate; 11 | 12 | @ApplicationScoped 13 | public class CustomMapping { 14 | 15 | private Book book = new Book("Java 11", LocalDate.now(), 1, false, "Duke", new BigDecimal(33.333)); 16 | 17 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 18 | JsonbConfig config = new JsonbConfig() 19 | .withAdapters(new BookAdapter()); 20 | 21 | Jsonb jsonb = JsonbBuilder.create(config); 22 | 23 | String jsonString = jsonb.toJson(book); 24 | System.out.println(jsonString); 25 | 26 | Book serializedBook = jsonb.fromJson(jsonString, Book.class); 27 | System.out.println(serializedBook); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /json-b/src/main/java/de/rieckpil/blog/ObjectMapping.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.bind.Jsonb; 7 | import javax.json.bind.JsonbBuilder; 8 | import java.math.BigDecimal; 9 | import java.time.LocalDate; 10 | 11 | @ApplicationScoped 12 | public class ObjectMapping { 13 | 14 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 15 | 16 | Book book = new Book("Java 11", LocalDate.now(), 1, false, "Duke", new BigDecimal(44.444)); 17 | 18 | Jsonb jsonb = JsonbBuilder.create(); 19 | 20 | String resultJson = jsonb.toJson(book); 21 | System.out.println(resultJson); 22 | 23 | Book serializedBook = jsonb.fromJson(resultJson, Book.class); 24 | System.out.println(serializedBook.toString()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /json-b/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /json-b/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /json-b/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /json-b/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /json-b/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /json-p/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /json-p/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/json-p.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /json-p/README.md: -------------------------------------------------------------------------------- 1 | # JSON-P: Java API for JSON Processing 2 | 3 | * [GitHub](https://github.com/eclipse-ee4j/jsonp) 4 | * [Spec](https://javaee.github.io/jsonp/) 5 | * Current version: **1.1** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: JSON Processing (JSON-P)](https://rieckpil.de/whatis-json-processing-json-p/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - JSON-P 1.1](https://youtu.be/2H7z_MbWwDQ) -------------------------------------------------------------------------------- /json-p/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/json-p . 4 | call docker rm -f json-p 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name json-p de.rieckpil.blog/json-p -------------------------------------------------------------------------------- /json-p/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/json-p . 3 | docker rm -f json-p || true && docker run -d -p 9080:9080 -p 9443:9443 --name json-p de.rieckpil.blog/json-p -------------------------------------------------------------------------------- /json-p/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | json-p 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /json-p/src/main/java/de/rieckpil/blog/ModifyJson.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.*; 7 | import java.io.StringReader; 8 | 9 | public class ModifyJson { 10 | 11 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 12 | jsonPatch(); 13 | jsonMergePatch(); 14 | jsonPointer(); 15 | } 16 | 17 | // read the official spec here: https://tools.ietf.org/html/rfc6901 18 | private void jsonPointer() { 19 | String jsonString = "{\"name\":\"duke\",\"age\":42,\"skills\":[\"Java SE\", \"Java EE\"]}"; 20 | 21 | JsonObject jsonObject = Json.createReader(new StringReader(jsonString)).readObject(); 22 | 23 | JsonPointer arrayElementPointer = Json.createPointer("/skills/1"); 24 | JsonPointer agePointer = Json.createPointer("/age"); 25 | JsonPointer namePointer = Json.createPointer("/name"); 26 | JsonPointer addressPointer = Json.createPointer("/address"); 27 | JsonPointer tagsPointer = Json.createPointer("/tags"); 28 | 29 | System.out.println("Get array element with pointer: " + arrayElementPointer.getValue(jsonObject).toString()); 30 | System.out.println("Remove age with pointer: " + agePointer.remove(jsonObject)); 31 | System.out.println("Replace name with pointer: " + namePointer.replace(jsonObject, Json.createValue("john"))); 32 | System.out.println("Check address with pointer: " + addressPointer.containsValue(jsonObject)); 33 | System.out.println("Add tags with pointer: " + tagsPointer.add(jsonObject, Json.createArrayBuilder().add("nice").build())); 34 | } 35 | 36 | // read the official spec here: https://tools.ietf.org/html/rfc7386 37 | private void jsonMergePatch() { 38 | String jsonString = "{\"name\":\"duke\",\"age\":42,\"skills\":[\"Java SE\", \"Java EE\"]}"; 39 | 40 | JsonObject jsonObject = Json.createReader(new StringReader(jsonString)).readObject(); 41 | 42 | JsonObject merge = Json.createObjectBuilder() 43 | .add("name", "duke2") 44 | .add("isEmployee", true) 45 | .add("skills", Json.createArrayBuilder() 46 | .add("CSS") 47 | .add("HTML") 48 | .add("JavaScript") 49 | .build()) 50 | .build(); 51 | 52 | JsonMergePatch mergePatch = Json.createMergePatch(merge); 53 | JsonValue mergedJson = mergePatch.apply(jsonObject); 54 | System.out.println("Merged JSON: " + mergedJson); 55 | } 56 | 57 | // read the official spec here: https://tools.ietf.org/html/rfc6902 58 | private void jsonPatch() { 59 | String jsonString = "{\"name\":\"duke\",\"age\":42,\"skills\":[\"Java SE\", \"Java EE\"]}"; 60 | 61 | JsonObject jsonObject = Json.createReader(new StringReader(jsonString)).readObject(); 62 | 63 | JsonPatch patch = Json.createPatchBuilder() 64 | .add("/isRetired", false) 65 | .add("/skills/2", "Jakarta EE") 66 | .remove("/age") 67 | .replace("/name", "duke two") 68 | .build(); 69 | 70 | JsonObject patchedJson = patch.apply(jsonObject); 71 | System.out.println("Patched JSON: " + patchedJson); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /json-p/src/main/java/de/rieckpil/blog/ReadingJson.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.Json; 7 | import javax.json.JsonArray; 8 | import javax.json.JsonObject; 9 | import javax.json.JsonReader; 10 | import java.io.StringReader; 11 | 12 | public class ReadingJson { 13 | 14 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 15 | readFromString(); 16 | readFromFile(); 17 | } 18 | 19 | private void readFromString() { 20 | JsonReader jsonReader = Json.createReader( 21 | new StringReader("{\"name\":\"duke\",\"age\":42,\"skills\":[\"Java SE\", \"Java EE\"]}")); 22 | JsonObject jsonObject = jsonReader.readObject(); 23 | System.out.println(jsonObject); 24 | } 25 | 26 | private void readFromFile() { 27 | JsonReader jsonReader = Json.createReader(this.getClass().getClassLoader() 28 | .getResourceAsStream("books.json")); 29 | JsonArray jsonArray = jsonReader.readArray(); 30 | System.out.println(jsonArray); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /json-p/src/main/java/de/rieckpil/blog/StreamingJson.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.Json; 7 | import javax.json.stream.JsonGenerator; 8 | import javax.json.stream.JsonParser; 9 | import java.io.StringReader; 10 | import java.io.StringWriter; 11 | 12 | import static javax.json.stream.JsonParser.Event; 13 | 14 | public class StreamingJson { 15 | 16 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 17 | parseJson(); 18 | generateJson(); 19 | } 20 | 21 | private void parseJson() { 22 | String jsonString = "{\"name\":\"duke\",\"isRetired\":false,\"age\":42,\"skills\":[\"Java SE\", \"Java EE\"]}"; 23 | try (JsonParser parser = Json.createParser(new StringReader(jsonString))) { 24 | while (parser.hasNext()) { 25 | final Event event = parser.next(); 26 | switch (event) { 27 | case START_ARRAY: 28 | System.out.println("Start of array"); 29 | break; 30 | case END_ARRAY: 31 | System.out.println("End of array"); 32 | break; 33 | case KEY_NAME: 34 | System.out.println("Key found " + parser.getString()); 35 | break; 36 | case VALUE_STRING: 37 | System.out.println("Value found " + parser.getString()); 38 | break; 39 | case VALUE_NUMBER: 40 | System.out.println("Number found " + parser.getLong()); 41 | break; 42 | case VALUE_TRUE: 43 | System.out.println(true); 44 | break; 45 | case VALUE_FALSE: 46 | System.out.println(false); 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | 53 | private void generateJson() { 54 | StringWriter stringWriter = new StringWriter(); 55 | 56 | try (JsonGenerator jsonGenerator = Json.createGenerator(stringWriter)) { 57 | jsonGenerator.writeStartArray() 58 | .writeStartObject() 59 | .write("name", "duke") 60 | .writeEnd() 61 | .writeStartObject() 62 | .write("name", "jakarta") 63 | .writeEnd() 64 | .writeEnd(); 65 | jsonGenerator.flush(); 66 | } 67 | 68 | System.out.println(stringWriter.toString()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /json-p/src/main/java/de/rieckpil/blog/WritingJson.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.json.*; 7 | import javax.json.stream.JsonGenerator; 8 | import java.io.*; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class WritingJson { 13 | 14 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) throws IOException { 15 | JsonObject json = Json.createObjectBuilder() 16 | .add("name", "Duke") 17 | .add("age", 42) 18 | .add("skills", 19 | Json.createArrayBuilder() 20 | .add("Java SE") 21 | .add("Java EE"). 22 | build()) 23 | .add("address", 24 | Json.createObjectBuilder() 25 | .add("street", "Mainstreet") 26 | .add("city", "Jakarta") 27 | .build()) 28 | .build(); 29 | 30 | JsonArray jsonArray = Json.createArrayBuilder() 31 | .add("foo") 32 | .add("bar") 33 | .add("duke") 34 | .build(); 35 | 36 | System.out.println(json); 37 | prettyPrintJsonToConsole(json); 38 | prettyPrintJsonToFile(json); 39 | } 40 | 41 | private void prettyPrintJsonToConsole(JsonObject json) throws IOException { 42 | Map config = new HashMap<>(); 43 | config.put(JsonGenerator.PRETTY_PRINTING, true); 44 | 45 | JsonWriterFactory writerFactory = Json.createWriterFactory(config); 46 | try (Writer stringWriter = new StringWriter(); 47 | JsonWriter jsonWriter = writerFactory.createWriter(stringWriter)) { 48 | jsonWriter.write(json); 49 | System.out.println(stringWriter); 50 | } 51 | } 52 | 53 | private void prettyPrintJsonToFile(JsonObject json) throws IOException { 54 | Map config = new HashMap<>(); 55 | config.put(JsonGenerator.PRETTY_PRINTING, true); 56 | 57 | JsonWriterFactory writerFactory = Json.createWriterFactory(config); 58 | try (OutputStream outputStream = new FileOutputStream(new File("/tmp/output.json")); 59 | JsonWriter jsonWriter = writerFactory.createWriter(outputStream)) { 60 | jsonWriter.write(json); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /json-p/src/main/resources/books.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Java 11", 4 | "author": "Duke", 5 | "isPublished": true, 6 | "pages": 110 7 | }, 8 | { 9 | "title": "Java 20", 10 | "author": "Duke", 11 | "isPublished": false, 12 | "pages": 200 13 | } 14 | ] -------------------------------------------------------------------------------- /json-p/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /json-p/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /json-p/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /json-p/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /json-p/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-config/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-config/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-config.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-config/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile Configuration 2 | 3 | Inject configuration properties from property files, environment variables, system variables and custom configuration sources 4 | 5 | * [GitHub](https://github.com/eclipse/microprofile-config) 6 | * [Specification](https://github.com/eclipse/microprofile-config/releases/download/1.3/microprofile-config-spec-1.3.pdf) 7 | * Current version: **1.3** in **MicroProfile 3.0** 8 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile Config](https://rieckpil.de/whatis-eclipse-microprofile-config/) 9 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile Config 1.3](https://www.youtube.com/watch?v=0h3QceSBBiY) -------------------------------------------------------------------------------- /microprofile-config/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-config . 4 | call docker rm -f microprofile-config 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-config de.rieckpil.blog/microprofile-config -------------------------------------------------------------------------------- /microprofile-config/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-config . 3 | docker rm -f microprofile-config || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-config de.rieckpil.blog/microprofile-config -------------------------------------------------------------------------------- /microprofile-config/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-config 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-config/src/main/java/de/rieckpil/blog/BasicConfigurationInjection.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.eclipse.microprofile.config.inject.ConfigProperty; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.enterprise.context.Initialized; 8 | import javax.enterprise.event.Observes; 9 | import javax.inject.Inject; 10 | import javax.inject.Provider; 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | public class BasicConfigurationInjection { 15 | 16 | @Inject 17 | private Config config; 18 | 19 | @Inject 20 | @ConfigProperty(name = "message", defaultValue = "Hello World") 21 | private String message; 22 | 23 | @Inject 24 | @ConfigProperty(name = "my.app.password") 25 | private Optional password; 26 | 27 | @Inject 28 | @ConfigProperty(name = "my.app.timeout") 29 | private Provider timeout; 30 | 31 | @Inject 32 | @ConfigProperty(name = "my.app.users") 33 | private List usersList; 34 | 35 | @Inject 36 | @ConfigProperty(name = "my.app.users") 37 | private String[] usersArray; 38 | 39 | @Inject 40 | @ConfigProperty(name = "my.app.token") 41 | private Token token; 42 | 43 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 44 | System.out.println(config.getValue("message", String.class)); 45 | System.out.println(message); 46 | System.out.println(password.orElseGet(() -> "DefaultPassword")); 47 | System.out.println(timeout.get()); 48 | usersList.forEach(u -> System.out.println(u)); 49 | System.out.println(usersArray); 50 | System.out.println(token); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /microprofile-config/src/main/java/de/rieckpil/blog/CustomConfigConverter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.config.spi.Converter; 4 | 5 | public class CustomConfigConverter implements Converter { 6 | 7 | @Override 8 | public Token convert(String value) { 9 | String[] chunks = value.split(","); 10 | Token result = new Token(chunks[0], chunks[1]); 11 | return result; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /microprofile-config/src/main/java/de/rieckpil/blog/CustomConfigObject.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.config.Config; 4 | import org.eclipse.microprofile.config.spi.ConfigProviderResolver; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.enterprise.context.Initialized; 8 | import javax.enterprise.event.Observes; 9 | 10 | public class CustomConfigObject { 11 | 12 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) { 13 | Config config = ConfigProviderResolver 14 | .instance() 15 | .getBuilder() 16 | .addDefaultSources() 17 | .withSources(new CustomConfigSource()) 18 | .addDiscoveredConverters() 19 | .build(); 20 | 21 | System.out.println("Customized config: " + config.getValue("message", String.class)); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /microprofile-config/src/main/java/de/rieckpil/blog/CustomConfigSource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.config.spi.ConfigSource; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | //config sources per default: System properties (ordinal=400) 9 | //config sources per default: Environment variables (ordinal=300) 10 | //config source to search in META-INF/microprofile-config.properties (ordinal=100) 11 | public class CustomConfigSource implements ConfigSource { 12 | 13 | public static final String CUSTOM_PASSWORD = "CUSTOM_PASSWORD"; 14 | public static final String MESSAGE = "Hello from custom ConfigSource"; 15 | 16 | @Override 17 | public int getOrdinal() { 18 | return 500; 19 | } 20 | 21 | @Override 22 | public Map getProperties() { 23 | Map properties = new HashMap<>(); 24 | properties.put("my.app.password", CUSTOM_PASSWORD); 25 | properties.put("message", MESSAGE); 26 | return properties; 27 | } 28 | 29 | @Override 30 | public String getValue(String key) { 31 | if (key.equalsIgnoreCase("my.app.password")) { 32 | return CUSTOM_PASSWORD; 33 | } else if (key.equalsIgnoreCase("message")) { 34 | return MESSAGE; 35 | } 36 | return null; 37 | } 38 | 39 | @Override 40 | public String getName() { 41 | return "randomConfigSource"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /microprofile-config/src/main/java/de/rieckpil/blog/Token.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | public class Token { 4 | 5 | private String name; 6 | private String payload; 7 | 8 | public Token(String name, String payload) { 9 | this.name = name; 10 | this.payload = payload; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public void setName(String name) { 18 | this.name = name; 19 | } 20 | 21 | public String getPayload() { 22 | return payload; 23 | } 24 | 25 | public void setPayload(String payload) { 26 | this.payload = payload; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "Token{" + 32 | "name='" + name + '\'' + 33 | ", payload='" + payload + '\'' + 34 | '}'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /microprofile-config/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | message=Hello World Java EE 8 2 | my.app.timeout=10 3 | my.app.users=Duke, John, Mike, Fred 4 | my.app.token=TOKEN_1337, SUPER_SECRET_VALUE 5 | -------------------------------------------------------------------------------- /microprofile-config/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource: -------------------------------------------------------------------------------- 1 | de.rieckpil.blog.CustomConfigSource -------------------------------------------------------------------------------- /microprofile-config/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.Converter: -------------------------------------------------------------------------------- 1 | de.rieckpil.blog.CustomConfigConverter -------------------------------------------------------------------------------- /microprofile-config/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-config/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-config/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-config/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-config/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-fault-tolerance.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile Fault Tolerance 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-fault-tolerance) 4 | * [Spec](https://github.com/eclipse/microprofile-fault-tolerance/releases/download/2.0/microprofile-fault-tolerance-spec-2.0.pdf) 5 | * Current version: **2.0** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile Fault Tolerance](https://rieckpil.de/whatis-eclipse-microprofile-fault-tolerance/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile Fault Tolerance 2.0](https://www.youtube.com/watch?v=_O4EjWHF0TQ) -------------------------------------------------------------------------------- /microprofile-fault-tolerance/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-fault-tolerance . 4 | call docker rm -f microprofile-fault-tolerance 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-fault-tolerance de.rieckpil.blog/microprofile-fault-tolerance -------------------------------------------------------------------------------- /microprofile-fault-tolerance/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-fault-tolerance . 3 | docker rm -f microprofile-fault-tolerance || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-fault-tolerance de.rieckpil.blog/microprofile-fault-tolerance -------------------------------------------------------------------------------- /microprofile-fault-tolerance/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-fault-tolerance 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/java/de/rieckpil/blog/PlaceHolderApiFallback.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.faulttolerance.ExecutionContext; 4 | import org.eclipse.microprofile.faulttolerance.FallbackHandler; 5 | 6 | import javax.json.Json; 7 | import javax.json.JsonObject; 8 | 9 | public class PlaceHolderApiFallback implements FallbackHandler { 10 | 11 | @Override 12 | public JsonObject handle(ExecutionContext context) { 13 | return Json.createObjectBuilder() 14 | .add("comment", "Lorem ipsum") 15 | .add("postId", Long.valueOf(context.getParameters()[0].toString())) 16 | .build(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/java/de/rieckpil/blog/RandomDataPrinter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.enterprise.context.Initialized; 5 | import javax.enterprise.event.Observes; 6 | import javax.inject.Inject; 7 | 8 | @ApplicationScoped 9 | public class RandomDataPrinter { 10 | 11 | @Inject 12 | private RandomDataProvider randomDataProvider; 13 | 14 | public void initialize(@Observes @Initialized(ApplicationScoped.class) Object init) throws InterruptedException { 15 | 16 | System.out.println(randomDataProvider.getPostById(1L)); 17 | System.out.println(randomDataProvider.accessFlakyService()); 18 | System.out.println(randomDataProvider.getDataFromLongRunningTask()); 19 | 20 | // for (int i = 0; i < 20; i++) { 21 | // System.out.println(randomDataProvider.getRandomData()); 22 | // Thread.sleep(500); 23 | // } 24 | // 25 | // for (int i = 0; i < 10; i++) { 26 | // final String threadName = "Thread" + i; 27 | // new Thread(() -> randomDataProvider.getConcurrentServiceData(threadName)).start(); 28 | // System.out.println(threadName + " started"); 29 | // } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/java/de/rieckpil/blog/RandomDataProvider.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.faulttolerance.*; 4 | 5 | import javax.annotation.PostConstruct; 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.json.Json; 8 | import javax.json.JsonObject; 9 | import javax.ws.rs.client.Client; 10 | import javax.ws.rs.client.ClientBuilder; 11 | import javax.ws.rs.client.WebTarget; 12 | import javax.ws.rs.core.MediaType; 13 | import java.time.LocalTime; 14 | import java.time.temporal.ChronoUnit; 15 | import java.util.concurrent.CompletableFuture; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.ThreadLocalRandom; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | @ApplicationScoped 21 | public class RandomDataProvider { 22 | 23 | private WebTarget webTarget; 24 | 25 | @PostConstruct 26 | public void setUp() { 27 | Client client = ClientBuilder.newBuilder() 28 | .connectTimeout(5, TimeUnit.SECONDS) 29 | .readTimeout(5, TimeUnit.SECONDS) 30 | .build(); 31 | 32 | this.webTarget = client 33 | .target("https://jsonplaceholder.typicode.com/posts"); 34 | } 35 | 36 | @Fallback(PlaceHolderApiFallback.class) 37 | // @Fallback(fallbackMethod = "getDefaultPost") 38 | public JsonObject getPostById(Long id) { 39 | return this.webTarget 40 | .path(String.valueOf(id)) 41 | .request() 42 | .accept(MediaType.APPLICATION_JSON) 43 | .get(JsonObject.class); 44 | } 45 | 46 | public JsonObject getDefaultPost(Long id) { 47 | return Json.createObjectBuilder() 48 | .add("comment", "Lorem ipsum") 49 | .add("postId", id) 50 | .build(); 51 | } 52 | 53 | @CircuitBreaker(successThreshold = 10, requestVolumeThreshold = 5, failureRatio = 0.5, delay = 500) 54 | @Fallback(fallbackMethod = "getFallbackData") 55 | public String getRandomData() { 56 | if (ThreadLocalRandom.current().nextLong(1000) < 300) { 57 | return "random duke"; 58 | } else { 59 | throw new RuntimeException("Random data not available"); 60 | } 61 | } 62 | 63 | @Timeout(value = 4, unit = ChronoUnit.SECONDS) 64 | @Fallback(fallbackMethod = "getFallbackData") 65 | public String getDataFromLongRunningTask() throws InterruptedException { 66 | Thread.sleep(4500); 67 | return "duke"; 68 | } 69 | 70 | @Retry(maxDuration = 5000, maxRetries = 3, delay = 500, jitter = 200) 71 | @Fallback(fallbackMethod = "getFallbackData") 72 | public String accessFlakyService() { 73 | 74 | System.out.println("Trying to access flaky service at " + LocalTime.now()); 75 | 76 | if (ThreadLocalRandom.current().nextLong(1000) < 50) { 77 | return "flaky duke"; 78 | } else { 79 | throw new RuntimeException("Flaky service not accessible"); 80 | } 81 | } 82 | 83 | @Bulkhead(5) 84 | @Asynchronous 85 | public Future getConcurrentServiceData(String name) { 86 | try { 87 | System.out.println(name + " is accessing the concurrent service"); 88 | Thread.sleep(2000); 89 | } catch (InterruptedException e) { 90 | e.printStackTrace(); 91 | } finally { 92 | return CompletableFuture.completedFuture("concurrent duke"); 93 | } 94 | } 95 | 96 | public String getFallbackData() { 97 | return "fallback duke"; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | de.rieckpil.blog.RandomDataProvider/accessFlakyService/Retry/maxRetries=10 2 | de.rieckpil.blog.RandomDataProvider/accessFlakyService/Retry/delay=300 3 | de.rieckpil.blog.RandomDataProvider/accessFlakyService/Retry/maxDuration=5000 4 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /microprofile-fault-tolerance/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-health/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-health/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-health.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-health/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile Health 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-health) 4 | * [Spec](https://github.com/eclipse/microprofile-health/releases/download/2.0.1/microprofile-health-spec.pdf) 5 | * Current version: **2.0.1** in **MicroProfile 3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile Health](https://rieckpil.de/whatis-eclipse-microprofile-health/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile Health 2.0](https://www.youtube.com/watch?v=nq_gdPUTx5c) -------------------------------------------------------------------------------- /microprofile-health/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-health . 4 | call docker rm -f microprofile-health 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-health de.rieckpil.blog/microprofile-health -------------------------------------------------------------------------------- /microprofile-health/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-health . 3 | docker rm -f microprofile-health || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-health de.rieckpil.blog/microprofile-health -------------------------------------------------------------------------------- /microprofile-health/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-health 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-health/src/main/java/de/rieckpil/blog/DiskSizeCheck.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.health.HealthCheck; 4 | import org.eclipse.microprofile.health.HealthCheckResponse; 5 | import org.eclipse.microprofile.health.HealthCheckResponseBuilder; 6 | import org.eclipse.microprofile.health.Liveness; 7 | 8 | import java.io.File; 9 | 10 | @Liveness 11 | public class DiskSizeCheck implements HealthCheck { 12 | 13 | @Override 14 | public HealthCheckResponse call() { 15 | 16 | File file = new File("/"); 17 | long freeSpace = file.getFreeSpace() / 1024 / 1024; 18 | 19 | HealthCheckResponseBuilder responseBuilder = HealthCheckResponse.builder() 20 | .name("diskSizeCheck") 21 | .withData("remainingSpace", freeSpace); 22 | 23 | return responseBuilder 24 | .state(freeSpace > 100) 25 | .build(); 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /microprofile-health/src/main/java/de/rieckpil/blog/FlakyLivenessCheck.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.health.HealthCheck; 4 | import org.eclipse.microprofile.health.HealthCheckResponse; 5 | import org.eclipse.microprofile.health.Liveness; 6 | 7 | import java.util.concurrent.ThreadLocalRandom; 8 | 9 | @Liveness 10 | public class FlakyLivenessCheck implements HealthCheck { 11 | 12 | @Override 13 | public HealthCheckResponse call() { 14 | 15 | return HealthCheckResponse 16 | .builder() 17 | .name("liveness") 18 | .state(ThreadLocalRandom.current().nextBoolean()) 19 | .build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /microprofile-health/src/main/java/de/rieckpil/blog/MultipleHealthCheck.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.health.HealthCheck; 4 | import org.eclipse.microprofile.health.HealthCheckResponse; 5 | import org.eclipse.microprofile.health.Liveness; 6 | import org.eclipse.microprofile.health.Readiness; 7 | 8 | @Readiness 9 | @Liveness 10 | public class MultipleHealthCheck implements HealthCheck { 11 | 12 | @Override 13 | public HealthCheckResponse call() { 14 | return HealthCheckResponse 15 | .builder() 16 | .name("generalCheck") 17 | .withData("foo", "bar") 18 | .withData("uptime", 42) 19 | .withData("isReady", true) 20 | .up() 21 | .build(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /microprofile-health/src/main/java/de/rieckpil/blog/ReadinessCheck.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.health.HealthCheck; 4 | import org.eclipse.microprofile.health.HealthCheckResponse; 5 | import org.eclipse.microprofile.health.Readiness; 6 | 7 | @Readiness 8 | public class ReadinessCheck implements HealthCheck { 9 | 10 | @Override 11 | public HealthCheckResponse call() { 12 | return HealthCheckResponse.builder() 13 | .name("readiness") 14 | .up() 15 | .build(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /microprofile-health/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-health/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-health/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-health/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-health/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | 24 | jwt-token.json 25 | jwtenizr-config.json 26 | microprofile-config.properties 27 | token.jwt 28 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-jwt-auth.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile JWT Auth 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-jwt-auth) 4 | * [Spec](https://github.com/eclipse/microprofile-jwt-auth/releases/download/1.1/microprofile-jwt-auth-spec-1.1.pdf) 5 | * Current version: **1.1** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile JWT Auth](https://rieckpil.de/whatis-eclipse-microprofile-jwt-auth/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile JWT Auth 1.1](https://youtu.be/8O3D2tNx1uM) 8 | 9 | ## Steps to run this project 10 | 11 | 1. Run `java -jar jwtenizr.jar` 12 | 2. Adjust the created `jwt-token.json` like the following: 13 | ```json 14 | { 15 | "iss": "rieckpil", 16 | "jti": "42", 17 | "sub": "duke", 18 | "upn": "duke", 19 | "groups": [ 20 | "chief", 21 | "hacker", 22 | "admin" 23 | ], 24 | "administrator_id": 42, 25 | "administrator_level": "HIGH" 26 | } 27 | ``` 28 | 3. Re-run `java -jar jwtenizr.jar` 29 | 4. Take the public key from the generated `microprofile-config.properties` file and paste it to `src/main/resources/META-INF/publicKey.pem` in the inner section, e.g.: 30 | ```shell script 31 | -----BEGIN RSA PUBLIC KEY----- 32 | MydoO3l7rOiRw5PMtlxHYRqK51eql2pVvp+lASalwIDAQAB 33 | -----END RSA PUBLIC KEY----- 34 | ``` 35 | 5. Start the application with `./buildAndRun.sh` or `buildAndRun.bat` 36 | 6. Use the cURL console output of the last run of `jwtenizr` and adjust the port and URL path, e.g.: 37 | ```shell script 38 | curl -i -H'Authorization: Bearer eyXYZ' http://localhost:9080/resources/books 39 | ``` 40 | or use Postman and take the JWT from the generated `token.jwt` file 41 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-jwt-auth . 4 | call docker rm -f microprofile-jwt-auth 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-jwt-auth de.rieckpil.blog/microprofile-jwt-auth -------------------------------------------------------------------------------- /microprofile-jwt-auth/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-jwt-auth . 3 | docker rm -f microprofile-jwt-auth || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-jwt-auth de.rieckpil.blog/microprofile-jwt-auth -------------------------------------------------------------------------------- /microprofile-jwt-auth/jwtenizr.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rieckpil/getting-started-with-microprofile/3c6984b2c26ec70919e0fd8779136161563960cb/microprofile-jwt-auth/jwtenizr.jar -------------------------------------------------------------------------------- /microprofile-jwt-auth/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-jwt-auth 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/java/de/rieckpil/blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.jwt.Claim; 4 | import org.eclipse.microprofile.jwt.JsonWebToken; 5 | 6 | import javax.annotation.security.RolesAllowed; 7 | import javax.enterprise.context.RequestScoped; 8 | import javax.inject.Inject; 9 | import javax.json.Json; 10 | import javax.json.JsonNumber; 11 | import javax.json.JsonObject; 12 | import javax.ws.rs.GET; 13 | import javax.ws.rs.Path; 14 | import javax.ws.rs.Produces; 15 | import javax.ws.rs.core.MediaType; 16 | import javax.ws.rs.core.Response; 17 | import java.security.Principal; 18 | 19 | @Path("books") 20 | @RequestScoped 21 | @Produces(MediaType.APPLICATION_JSON) 22 | public class BookResource { 23 | 24 | @Inject 25 | private Principal principal; 26 | 27 | @Inject 28 | private JsonWebToken jsonWebToken; 29 | 30 | @Inject 31 | @Claim("administrator_id") 32 | private JsonNumber administrator_id; 33 | 34 | @GET 35 | @RolesAllowed("admin") 36 | public Response getBook() { 37 | 38 | System.out.println("Secret book for " + principal.getName() + " with roles " + jsonWebToken.getGroups()); 39 | System.out.println("Administrator level: " + jsonWebToken.getClaim("administrator_level").toString()); 40 | System.out.println("Administrator id: " + administrator_id); 41 | 42 | JsonObject secretBook = Json.createObjectBuilder() 43 | .add("title", "secret") 44 | .add("author", "duke") 45 | .build(); 46 | 47 | return Response.ok(secretBook).build(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/java/de/rieckpil/blog/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.auth.LoginConfig; 4 | 5 | import javax.ws.rs.ApplicationPath; 6 | import javax.ws.rs.core.Application; 7 | 8 | @ApplicationPath("resources") 9 | @LoginConfig(authMethod = "MP-JWT") 10 | public class JAXRSConfiguration extends Application { 11 | } 12 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/java/de/rieckpil/blog/OrderResources.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.security.PermitAll; 4 | import javax.json.Json; 5 | import javax.json.JsonObject; 6 | import javax.ws.rs.GET; 7 | import javax.ws.rs.Path; 8 | import javax.ws.rs.Produces; 9 | import javax.ws.rs.core.MediaType; 10 | import javax.ws.rs.core.Response; 11 | 12 | @Path("orders") 13 | @Produces(MediaType.APPLICATION_JSON) 14 | public class OrderResources { 15 | 16 | @GET 17 | @PermitAll 18 | public Response getOrder() { 19 | JsonObject order = Json.createObjectBuilder() 20 | .add("amount", 42) 21 | .add("product", "bike") 22 | .build(); 23 | 24 | return Response.ok(order).build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | mp.jwt.verify.publickey.location=/META-INF/publicKey.pem 2 | mp.jwt.verify.issuer=rieckpil 3 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/resources/META-INF/publicKey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PUBLIC KEY----- 2 | ENTER_PUBLIC_KEY_HERE 3 | -----END RSA PUBLIC KEY----- -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-jwt-auth/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-metrics/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-metrics/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-metrics.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config/ 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-metrics/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile Metrics 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-metrics) 4 | * [Spec](https://github.com/eclipse/microprofile-metrics/releases/download/2.0/microprofile-metrics-spec-2.0.pdf) 5 | * Current version: **2.0** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile Metrics](https://rieckpil.de/whatis-eclipse-microprofile-metrics/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile Metrics 2.0](https://www.youtube.com/watch?v=jI6DoNYVd-U) -------------------------------------------------------------------------------- /microprofile-metrics/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-metrics . 4 | call docker rm -f microprofile-metrics 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-metrics de.rieckpil.blog/microprofile-metrics 6 | call docker logs -f microprofile-metrics -------------------------------------------------------------------------------- /microprofile-metrics/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-metrics . 3 | docker rm -f microprofile-metrics || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-metrics de.rieckpil.blog/microprofile-metrics -------------------------------------------------------------------------------- /microprofile-metrics/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | microprofile-metrics 14 | 1.0-SNAPSHOT 15 | war 16 | 17 | 18 | ${project.artifactId} 19 | 20 | 21 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/java/de/rieckpil/blog/BookCommentClient.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.metrics.MetricRegistry; 4 | import org.eclipse.microprofile.metrics.annotation.Counted; 5 | import org.eclipse.microprofile.metrics.annotation.RegistryType; 6 | import org.eclipse.microprofile.metrics.annotation.Timed; 7 | 8 | import javax.annotation.PostConstruct; 9 | import javax.inject.Inject; 10 | import javax.json.JsonObject; 11 | import javax.ws.rs.client.Client; 12 | import javax.ws.rs.client.ClientBuilder; 13 | import javax.ws.rs.client.WebTarget; 14 | import javax.ws.rs.core.Response; 15 | import java.util.concurrent.ThreadLocalRandom; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | public class BookCommentClient { 19 | 20 | @Inject 21 | @RegistryType(type = MetricRegistry.Type.APPLICATION) 22 | private MetricRegistry metricRegistry; 23 | 24 | private WebTarget bookCommentsWebTarget; 25 | 26 | @Counted(name = "bookCommentClientInvocations", description = "Counting the invocations of the constructor") 27 | public BookCommentClient() { 28 | } 29 | 30 | @PostConstruct 31 | public void setUp() { 32 | Client client = ClientBuilder 33 | .newBuilder() 34 | .connectTimeout(5, TimeUnit.SECONDS) 35 | .readTimeout(5, TimeUnit.SECONDS) 36 | .build(); 37 | 38 | this.bookCommentsWebTarget = client.target("https://jsonplaceholder.typicode.com/comments"); 39 | } 40 | 41 | @Timed(name = "getBookCommentByBookIdDuration") 42 | //@SimplyTimed(name = "getBookCommentByBookIdDuration") 43 | public String getBookCommentByBookId(String bookId) { 44 | this.sleepRandom(); 45 | 46 | Response response = this.bookCommentsWebTarget.path(bookId).request().get(); 47 | 48 | this.metricRegistry.counter("bookCommentApiResponseCode" + response.getStatus()).inc(); 49 | 50 | return response.readEntity(JsonObject.class).getString("body"); 51 | } 52 | 53 | private void sleepRandom() { 54 | try { 55 | Thread.sleep(ThreadLocalRandom.current().nextLong(1000)); 56 | } catch (InterruptedException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/java/de/rieckpil/blog/BookRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.metrics.annotation.Gauge; 4 | 5 | import javax.enterprise.context.ApplicationScoped; 6 | import java.util.concurrent.ThreadLocalRandom; 7 | 8 | @ApplicationScoped 9 | public class BookRequestProcessor { 10 | 11 | @Gauge(unit = "amount") 12 | public Long remainingBookRequestsToProcess() { 13 | // monitor e.g. current size of a JMS queue 14 | return ThreadLocalRandom.current().nextLong(0, 1_000_000); 15 | } 16 | 17 | public String getLatestBookRequestId() { 18 | return String.valueOf(ThreadLocalRandom.current().nextLong(10)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/java/de/rieckpil/blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.metrics.annotation.Metered; 4 | 5 | import javax.enterprise.context.RequestScoped; 6 | import javax.inject.Inject; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.Path; 9 | import javax.ws.rs.Produces; 10 | import javax.ws.rs.core.MediaType; 11 | import javax.ws.rs.core.Response; 12 | 13 | @RequestScoped 14 | @Path("books") 15 | public class BookResource { 16 | 17 | @Inject 18 | private BookCommentClient bookCommentClient; 19 | 20 | @Inject 21 | private BookRequestProcessor bookRequestProcessor; 22 | 23 | @GET 24 | @Metered(name = "getBookCommentForLatestBookRequest", tags = {"spec=JAX-RS", "level=REST"}) 25 | @Produces(MediaType.TEXT_PLAIN) 26 | public Response getBookCommentForLatestBookRequest() { 27 | String latestBookRequestId = bookRequestProcessor.getLatestBookRequestId(); 28 | return Response.ok(this.bookCommentClient.getBookCommentByBookId(latestBookRequestId)).build(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/java/de/rieckpil/blog/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSConfiguration extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | message=Hello World Java EE 8 -------------------------------------------------------------------------------- /microprofile-metrics/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-metrics/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-metrics/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-metrics/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-open-api/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-open-api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-open-api.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config/ 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-open-api/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile OpenAPI 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-open-api) 4 | * [Spec](https://download.eclipse.org/microprofile/microprofile-open-api-1.1/microprofile-openapi-spec.pdf) 5 | * Current version: **1.1** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile OpenAPI](https://rieckpil.de/whatis-eclipse-microprofile-openapi/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile OpenAPI 1.1](https://www.youtube.com/watch?v=Rn7T26UW_H8) 8 | -------------------------------------------------------------------------------- /microprofile-open-api/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blog/microprofile-open-api . 4 | call docker rm -f microprofile-open-api 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-open-api de.rieckpil.blog/microprofile-open-api 6 | call docker logs -f microprofile-open-api -------------------------------------------------------------------------------- /microprofile-open-api/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-open-api . 3 | docker rm -f microprofile-open-api || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-open-api de.rieckpil.blog/microprofile-open-api && docker logs -f microprofile-open-api -------------------------------------------------------------------------------- /microprofile-open-api/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-open-api 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/java/de/rieckpil/blog/Book.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 4 | 5 | @Schema(name = "Book", description = "POJO that represents a book.") 6 | public class Book { 7 | 8 | @Schema(required = true, example = "MicroProfile") 9 | private String title; 10 | 11 | @Schema(required = true, example = "Duke") 12 | private String author; 13 | 14 | @Schema(required = true, readOnly = true, example = "1") 15 | private Long id; 16 | 17 | public Book() { 18 | } 19 | 20 | public Book(String title, String author, Long id) { 21 | this.title = title; 22 | this.author = author; 23 | this.id = id; 24 | } 25 | 26 | public String getTitle() { 27 | return title; 28 | } 29 | 30 | public void setTitle(String title) { 31 | this.title = title; 32 | } 33 | 34 | public String getAuthor() { 35 | return author; 36 | } 37 | 38 | public void setAuthor(String author) { 39 | this.author = author; 40 | } 41 | 42 | public Long getId() { 43 | return id; 44 | } 45 | 46 | public void setId(Long id) { 47 | this.id = id; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/java/de/rieckpil/blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.openapi.annotations.Operation; 4 | import org.eclipse.microprofile.openapi.annotations.media.Content; 5 | import org.eclipse.microprofile.openapi.annotations.media.Schema; 6 | import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; 7 | import org.eclipse.microprofile.openapi.annotations.tags.Tag; 8 | 9 | import javax.json.JsonObject; 10 | import javax.ws.rs.*; 11 | import javax.ws.rs.core.Context; 12 | import javax.ws.rs.core.MediaType; 13 | import javax.ws.rs.core.Response; 14 | import javax.ws.rs.core.UriInfo; 15 | 16 | @Path("books") 17 | public class BookResource { 18 | 19 | @GET 20 | @Operation(summary = "Get all books", description = "Returns all available books of the book store XYZ") 21 | @APIResponse(responseCode = "404", description = "No books found") 22 | @APIResponse(responseCode = "418", description = "I'm a teapot") 23 | @APIResponse(responseCode = "500", description = "Server unavailable") 24 | @Tag(name = "BETA", description = "This API is currently in beta state") 25 | @Produces(MediaType.APPLICATION_JSON) 26 | public Response getAllBooks() { 27 | System.out.println("Get all books..."); 28 | return Response.ok(new Book("MicroProfile", "Duke", 1L)).build(); 29 | } 30 | 31 | @GET 32 | @APIResponse(description = "Book", 33 | content = @Content(mediaType = "application/json", 34 | schema = @Schema(implementation = Book.class))) 35 | @Path("/{id}") 36 | @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 37 | public Response getBookById(@PathParam("id") Long id) { 38 | System.out.println("Get book by id..."); 39 | return Response.ok(new Book("MicroProfile", "Duke", 1L)).build(); 40 | } 41 | 42 | @POST 43 | public Response createNewBook(JsonObject jsonObject, @Context UriInfo uriInfo) { 44 | System.out.println("Creating new book..."); 45 | return Response.created(uriInfo.getAbsolutePathBuilder().build()).build(); 46 | } 47 | 48 | @DELETE 49 | @Path("/{id}") 50 | public Response deleteBookById(@PathParam("id") Long id) { 51 | System.out.println("Deleting book..."); 52 | return Response.noContent().build(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/java/de/rieckpil/blog/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSConfiguration extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-open-api/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-open-api/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-open-api/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-open-tracing/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile OpenTracing 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-opentracing) 4 | * [Spec](https://github.com/eclipse/microprofile-opentracing/releases/download/1.3/microprofile-opentracing-spec-1.3.pdf) 5 | * Current version: **1.3** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile OpenTracing](https://rieckpil.de/whatis-eclipse-microprofile-opentracing/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile OpenTracing 1.3](https://www.youtube.com/watch?v=b43XgElBxEo) 8 | 9 | ## Steps to run this example 10 | 11 | 1. Ensure your Docker daemon is running (required Docker engine: 18.02.0+) 12 | 2. Execute either `buildAndRun.bat` (Windows) or `./buildAndRun.sh` (Linux & Mac) to build both projects and start the everything with `docker-compose` 13 | 3. Wait until everything is up and running 14 | 4. Visit http://localhost:9080/resources/books in your browser (response takes up to 1 - 3 seconds) 15 | 5. Visit http://localhost:9411/zipkin/ in your browser and search for available traces -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-open-tracing-client.war /config/dropins/ 4 | COPY --chown=1001:0 server.xml /config/ 5 | COPY --chown=1001:0 extension /opt/ol/wlp/usr/extension 6 | 7 | RUN configure.sh 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/extension/lib/com.ibm.ws.io.opentracing.zipkintracer-0.31.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rieckpil/getting-started-with-microprofile/3c6984b2c26ec70919e0fd8779136161563960cb/microprofile-open-tracing/book-store-client/extension/lib/com.ibm.ws.io.opentracing.zipkintracer-0.31.jar -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/extension/lib/features/opentracingZipkin-0.31.mf: -------------------------------------------------------------------------------- 1 | IBM-Feature-Version: 2 2 | IBM-ShortName: opentracingZipkin-0.31 3 | Subsystem-Content: com.ibm.websphere.appserver.opentracing-1.1; ibm.tolerates:="1.2,1.3"; type="osgi.subsystem.feature", 4 | com.ibm.ws.opentracing.zipkin; version="[1.3.0,1.3.200)", 5 | com.ibm.websphere.appserver.jaxrs-2.0; type="osgi.subsystem.feature"; ibm.tolerates:=2.1, 6 | com.ibm.websphere.appserver.cdi-1.2; type="osgi.subsystem.feature"; ibm.tolerates:=2.0 7 | Subsystem-Description: %description 8 | Subsystem-License: https://www.eclipse.org/legal/epl-v10.html 9 | Subsystem-Localization: OSGI-INF/l10n/com.ibm.websphere.appserver.opentracingZipkin-0.31 10 | Subsystem-ManifestVersion: 1 11 | Subsystem-Name: Opentracing Zipkin Tracer implementation 12 | Subsystem-SymbolicName: com.ibm.websphere.appserver.opentracingZipkin-0.31; visibility:=public; singleton:=true 13 | Subsystem-Type: osgi.subsystem.feature 14 | Subsystem-Version: 1.0.0 15 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-open-tracing-client 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | microProfile-3.3 6 | usr:opentracingZipkin-0.31 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/java/de/rieckpil/blog/BookProvider.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.annotation.PreDestroy; 5 | import javax.enterprise.context.RequestScoped; 6 | import javax.inject.Inject; 7 | import javax.json.Json; 8 | import javax.json.JsonArray; 9 | import javax.json.JsonObject; 10 | import javax.json.JsonValue; 11 | import javax.json.stream.JsonCollectors; 12 | import javax.ws.rs.client.Client; 13 | import javax.ws.rs.client.ClientBuilder; 14 | import javax.ws.rs.client.WebTarget; 15 | import javax.ws.rs.core.MediaType; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | @RequestScoped 21 | public class BookProvider { 22 | 23 | @Inject 24 | private PriceCalculator priceCalculator; 25 | 26 | private WebTarget bookStoreTarget; 27 | private Client client; 28 | 29 | @PostConstruct 30 | public void setup() { 31 | this.client = ClientBuilder 32 | .newBuilder() 33 | .connectTimeout(2, TimeUnit.SECONDS) 34 | .readTimeout(2, TimeUnit.SECONDS) 35 | .build(); 36 | 37 | this.bookStoreTarget = client.target("http://book-store:9080/resources/books"); 38 | } 39 | 40 | public JsonArray getBooksFromBookStore() { 41 | 42 | JsonArray books = this.bookStoreTarget 43 | .request() 44 | .accept(MediaType.APPLICATION_JSON) 45 | .get() 46 | .readEntity(JsonArray.class); 47 | 48 | List result = new ArrayList(); 49 | 50 | for (JsonObject book : books.getValuesAs(JsonValue::asJsonObject)) { 51 | result.add(Json.createObjectBuilder() 52 | .add("title", book.getString("title")) 53 | .add("price", priceCalculator.getPriceForBook(book.getInt("id"))) 54 | .build()); 55 | } 56 | 57 | return result 58 | .stream() 59 | .collect(JsonCollectors.toJsonArray()); 60 | } 61 | 62 | @PreDestroy 63 | public void tearDown() { 64 | this.client.close(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/java/de/rieckpil/blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.inject.Inject; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.Path; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | import javax.ws.rs.core.Response; 9 | 10 | @Path("books") 11 | public class BookResource { 12 | 13 | @Inject 14 | private BookProvider bookProvider; 15 | 16 | @GET 17 | @Produces(MediaType.APPLICATION_JSON) 18 | public Response getBooks() { 19 | return Response.ok(bookProvider.getBooksFromBookStore()).build(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/java/de/rieckpil/blog/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSConfiguration extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/java/de/rieckpil/blog/PriceCalculator.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.enterprise.context.RequestScoped; 5 | import javax.ws.rs.client.Client; 6 | import javax.ws.rs.client.ClientBuilder; 7 | import javax.ws.rs.client.WebTarget; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @RequestScoped 11 | public class PriceCalculator { 12 | 13 | private WebTarget bookStorePriceTarget; 14 | private Double discount = 1.5; 15 | 16 | @PostConstruct 17 | public void setUp() { 18 | Client client = ClientBuilder 19 | .newBuilder() 20 | .connectTimeout(2, TimeUnit.SECONDS) 21 | .readTimeout(2, TimeUnit.SECONDS) 22 | .build(); 23 | 24 | this.bookStorePriceTarget = client.target("http://book-store:9080/resources/prices"); 25 | } 26 | 27 | public Double getPriceForBook(int id) { 28 | Double bookPrice = this.bookStorePriceTarget.path(String.valueOf(id)).request().get().readEntity(Double.class); 29 | return Math.round((bookPrice - discount) * 100.0) / 100.0; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store-client/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-open-tracing-server.war /config/dropins/ 4 | COPY --chown=1001:0 server.xml /config/ 5 | COPY --chown=1001:0 extension /opt/ol/wlp/usr/extension 6 | 7 | RUN configure.sh 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/extension/lib/com.ibm.ws.io.opentracing.zipkintracer-0.31.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rieckpil/getting-started-with-microprofile/3c6984b2c26ec70919e0fd8779136161563960cb/microprofile-open-tracing/book-store/extension/lib/com.ibm.ws.io.opentracing.zipkintracer-0.31.jar -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/extension/lib/features/opentracingZipkin-0.31.mf: -------------------------------------------------------------------------------- 1 | IBM-Feature-Version: 2 2 | IBM-ShortName: opentracingZipkin-0.31 3 | Subsystem-Content: com.ibm.websphere.appserver.opentracing-1.1; ibm.tolerates:="1.2,1.3"; type="osgi.subsystem.feature", 4 | com.ibm.ws.opentracing.zipkin; version="[1.3.0,1.3.200)", 5 | com.ibm.websphere.appserver.jaxrs-2.0; type="osgi.subsystem.feature"; ibm.tolerates:=2.1, 6 | com.ibm.websphere.appserver.cdi-1.2; type="osgi.subsystem.feature"; ibm.tolerates:=2.0 7 | Subsystem-Description: %description 8 | Subsystem-License: https://www.eclipse.org/legal/epl-v10.html 9 | Subsystem-Localization: OSGI-INF/l10n/com.ibm.websphere.appserver.opentracingZipkin-0.31 10 | Subsystem-ManifestVersion: 1 11 | Subsystem-Name: Opentracing Zipkin Tracer implementation 12 | Subsystem-SymbolicName: com.ibm.websphere.appserver.opentracingZipkin-0.31; visibility:=public; singleton:=true 13 | Subsystem-Type: osgi.subsystem.feature 14 | Subsystem-Version: 1.0.0 15 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-open-tracing-server 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | microProfile-3.3 6 | usr:opentracingZipkin-0.31 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/java/de/rieckpil/blog/BookResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.annotation.PostConstruct; 4 | import javax.enterprise.context.ApplicationScoped; 5 | import javax.json.Json; 6 | import javax.json.JsonArray; 7 | import javax.json.JsonObject; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.Produces; 11 | import javax.ws.rs.core.MediaType; 12 | import javax.ws.rs.core.Response; 13 | 14 | @Path("books") 15 | @ApplicationScoped 16 | public class BookResource { 17 | 18 | private JsonArray books; 19 | 20 | @PostConstruct 21 | public void setup() { 22 | 23 | JsonObject bookOne = Json.createObjectBuilder() 24 | .add("id", 1) 25 | .add("title", "MicroProfile 3.0") 26 | .build(); 27 | 28 | JsonObject bookTwo = Json.createObjectBuilder() 29 | .add("id", 2) 30 | .add("title", "Jakarta EE 8") 31 | .build(); 32 | 33 | JsonObject bookThree = Json.createObjectBuilder() 34 | .add("id", 3) 35 | .add("title", "Java 13") 36 | .build(); 37 | 38 | this.books = Json.createArrayBuilder() 39 | .add(bookOne) 40 | .add(bookTwo) 41 | .add(bookThree) 42 | .build(); 43 | 44 | } 45 | 46 | @GET 47 | @Produces(MediaType.APPLICATION_JSON) 48 | public Response getAvailableBooks() { 49 | return Response.ok(this.books).build(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/java/de/rieckpil/blog/ContainerLoggingRequestFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.container.ContainerRequestContext; 4 | import javax.ws.rs.container.ContainerRequestFilter; 5 | import javax.ws.rs.ext.Provider; 6 | import java.time.LocalDateTime; 7 | 8 | @Provider 9 | public class ContainerLoggingRequestFilter implements ContainerRequestFilter { 10 | 11 | @Override 12 | public void filter(ContainerRequestContext requestContext) { 13 | System.out.println("=============="); 14 | System.out.println("Incoming request at: " + LocalDateTime.now()); 15 | requestContext.getHeaders().forEach((k, v) -> System.out.println(k + ":" + v)); 16 | System.out.println("=============="); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/java/de/rieckpil/blog/JAXRSConfiguration.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.ApplicationPath; 4 | import javax.ws.rs.core.Application; 5 | 6 | @ApplicationPath("resources") 7 | public class JAXRSConfiguration extends Application { 8 | } 9 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/java/de/rieckpil/blog/PriceResource.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import javax.ws.rs.GET; 5 | import javax.ws.rs.Path; 6 | import javax.ws.rs.PathParam; 7 | import javax.ws.rs.Produces; 8 | import javax.ws.rs.core.MediaType; 9 | import javax.ws.rs.core.Response; 10 | import java.util.concurrent.ThreadLocalRandom; 11 | 12 | @Path("prices") 13 | @ApplicationScoped 14 | public class PriceResource { 15 | 16 | @GET 17 | @Path("/{id}") 18 | @Produces(MediaType.TEXT_PLAIN) 19 | public Response getPriceForBook(@PathParam("id") Integer bookId) { 20 | System.out.println("Retrieving price for book with id: " + bookId); 21 | sleepRandom(); 22 | return Response.ok(ThreadLocalRandom.current().nextDouble(20.0, 42.0)).build(); 23 | } 24 | 25 | private void sleepRandom() { 26 | try { 27 | Thread.sleep(ThreadLocalRandom.current().nextLong(100, 500)); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /microprofile-open-tracing/book-store/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /microprofile-open-tracing/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean -f book-store/pom.xml package 3 | call mvn clean -f book-store-client/pom.xml package 4 | call docker-compose build 5 | call docker-compose up --force-recreate -------------------------------------------------------------------------------- /microprofile-open-tracing/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean -f book-store/pom.xml package && mvn clean -f book-store-client/pom.xml package 3 | docker-compose build && docker-compose up --force-recreate -------------------------------------------------------------------------------- /microprofile-open-tracing/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | book-store-client: 4 | build: book-store-client/ 5 | ports: 6 | - "9080:9080" 7 | - "9443:9443" 8 | links: 9 | - zipkin 10 | - book-store 11 | book-store: 12 | build: book-store/ 13 | links: 14 | - zipkin 15 | zipkin: 16 | image: openzipkin/zipkin 17 | ports: 18 | - "9411:9411" 19 | -------------------------------------------------------------------------------- /microprofile-rest-client/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | .sts4-cache 11 | 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | /nbproject/private/ 18 | /build/ 19 | /nbbuild/ 20 | /dist/ 21 | /nbdist/ 22 | /.nb-gradle/ 23 | -------------------------------------------------------------------------------- /microprofile-rest-client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openliberty/open-liberty:20.0.0.5-kernel-java11-openj9-ubi 2 | 3 | COPY --chown=1001:0 target/microprofile-rest-client.war /config/dropins/ 4 | COPY --chown=1001:0 target/server.xml /config 5 | 6 | RUN configure.sh 7 | -------------------------------------------------------------------------------- /microprofile-rest-client/README.md: -------------------------------------------------------------------------------- 1 | # MicroProfile Rest Client 2 | 3 | * [GitHub](https://github.com/eclipse/microprofile-rest-client) 4 | * [Spec](https://download.eclipse.org/microprofile/microprofile-rest-client-1.3/microprofile-rest-client-1.3.pdf) 5 | * Current version: **1.3** in MicroProfile **3.0** 6 | * Detailed blog post about this specification: [#WHATIS?: MicroProfile Rest Client](https://rieckpil.de/whatis-eclipse-microprofile-rest-client/) 7 | * YouTube video about this specification: [Getting started with Eclipse MicroProfile 3.0 - MicroProfile Rest Client 1.3](https://youtu.be/HJWxI_T3FKo) -------------------------------------------------------------------------------- /microprofile-rest-client/buildAndRun.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | call mvn clean package 3 | call docker build -t de.rieckpil.blogmicroprofile-rest-client . 4 | call docker rm -f microprofile-rest-client 5 | call docker run -d -p 9080:9080 -p 9443:9443 --name mmicroprofile-rest-client de.rieckpil.blog/microprofile-rest-client -------------------------------------------------------------------------------- /microprofile-rest-client/buildAndRun.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mvn clean package && docker build -t de.rieckpil.blog/microprofile-rest-client . 3 | docker rm -f microprofile-rest-client || true && docker run -d -p 9080:9080 -p 9443:9443 --name microprofile-rest-client de.rieckpil.blog/microprofile-rest-client -------------------------------------------------------------------------------- /microprofile-rest-client/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | ../pom.xml 10 | 11 | 12 | 4.0.0 13 | de.rieckpil.blog 14 | microprofile-rest-client 15 | 1.0-SNAPSHOT 16 | war 17 | 18 | 19 | ${project.artifactId} 20 | 21 | 22 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/java/de/rieckpil/blog/GlobalClientHeaders.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.config.inject.ConfigProperty; 4 | import org.eclipse.microprofile.rest.client.ext.ClientHeadersFactory; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.inject.Inject; 8 | import javax.ws.rs.core.MultivaluedHashMap; 9 | import javax.ws.rs.core.MultivaluedMap; 10 | 11 | @ApplicationScoped 12 | public class GlobalClientHeaders implements ClientHeadersFactory { 13 | 14 | @Inject 15 | @ConfigProperty(name = "secrets.value") 16 | private String secretValue; 17 | 18 | @Override 19 | public MultivaluedMap update(MultivaluedMap incomingHeaders, MultivaluedMap clientOutgoingHeaders) { 20 | 21 | System.out.println("--- Incoming headers of the JAX-RS environment"); 22 | incomingHeaders.forEach((k, v) -> System.out.println(k + ":" + v)); 23 | 24 | System.out.println("--- Specified outgoing headers of the Rest Client"); 25 | clientOutgoingHeaders.forEach((k, v) -> System.out.println(k + ":" + v)); 26 | 27 | MultivaluedMap resultHeader = new MultivaluedHashMap(); 28 | resultHeader.putAll(incomingHeaders); 29 | resultHeader.putAll(clientOutgoingHeaders); 30 | 31 | resultHeader.add("X-Secret-Header", secretValue); 32 | resultHeader.add("X-Global-Header", "duke"); 33 | resultHeader.add("X-Special-Header", "MicroProfile"); 34 | 35 | System.out.println("--- Header of the Rest Client after merging"); 36 | resultHeader.forEach((k, v) -> System.out.println(k + ":" + v)); 37 | 38 | return resultHeader; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/java/de/rieckpil/blog/JSONPlaceholderClient.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.rest.client.annotation.ClientHeaderParam; 4 | import org.eclipse.microprofile.rest.client.annotation.RegisterClientHeaders; 5 | import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; 6 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; 7 | 8 | import javax.json.JsonArray; 9 | import javax.json.JsonObject; 10 | import javax.ws.rs.*; 11 | import javax.ws.rs.core.MediaType; 12 | import javax.ws.rs.core.Response; 13 | import java.util.Base64; 14 | import java.util.concurrent.CompletionStage; 15 | 16 | @RegisterRestClient 17 | @Consumes(MediaType.APPLICATION_JSON) 18 | @Produces(MediaType.APPLICATION_JSON) 19 | @RegisterProvider(ResponseLoggingFilter.class) 20 | @RegisterClientHeaders(GlobalClientHeaders.class) 21 | @ClientHeaderParam(name = "X-Application-Name", value = "MP-blog") 22 | public interface JSONPlaceholderClient { 23 | 24 | @GET 25 | @Path("/posts") 26 | JsonArray getAllPosts(@QueryParam("orderBy") String orderDirection); 27 | 28 | @GET 29 | @Path("/posts/{id}") 30 | CompletionStage getPostById(@PathParam("id") String id); 31 | 32 | @GET 33 | @Path("/posts/{id}/comments") 34 | JsonArray getCommentsForPostByPostId(@PathParam("id") String id); 35 | 36 | @POST 37 | @Path("/posts") 38 | Response createPost(JsonObject post); 39 | 40 | @DELETE 41 | @Path("/posts/{id}") 42 | Response deletePostById(@PathParam("id") String id); 43 | 44 | @PUT 45 | @ClientHeaderParam(name = "Authorization", value = "{generateAuthHeader}") 46 | @Path("/posts/{id}") 47 | Response updatePostById(@PathParam("id") String id, JsonObject post, @HeaderParam("X-Request-Id") String requestIdHeader); 48 | 49 | default String generateAuthHeader() { 50 | return "Basic " + new String(Base64.getEncoder().encode("duke:SECRET".getBytes())); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/java/de/rieckpil/blog/PostService.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.eclipse.microprofile.rest.client.RestClientBuilder; 4 | import org.eclipse.microprofile.rest.client.inject.RestClient; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | import javax.enterprise.context.Initialized; 8 | import javax.enterprise.event.Observes; 9 | import javax.inject.Inject; 10 | import javax.json.Json; 11 | import javax.json.JsonObject; 12 | import javax.ws.rs.core.Response; 13 | import java.net.URI; 14 | import java.net.URISyntaxException; 15 | import java.util.UUID; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | @ApplicationScoped 19 | public class PostService { 20 | 21 | @Inject 22 | @RestClient 23 | JSONPlaceholderClient jsonPlaceholderClient; 24 | 25 | public void init(@Observes @Initialized(ApplicationScoped.class) Object init) throws URISyntaxException { 26 | restClientBuilderExample(); 27 | // getAllPosts(); 28 | // getSinglePost(); 29 | // createNewPost(); 30 | // updateExistingPost(); 31 | // deletePost(); 32 | } 33 | 34 | private void restClientBuilderExample() throws URISyntaxException { 35 | System.out.println("------ Rest Client builder example ------"); 36 | 37 | JSONPlaceholderClient jsonApiClient = RestClientBuilder.newBuilder() 38 | .baseUri(new URI("https://jsonplaceholder.typicode.com")) 39 | .register(ResponseLoggingFilter.class) 40 | .connectTimeout(2, TimeUnit.SECONDS) 41 | .readTimeout(2, TimeUnit.SECONDS) 42 | .build(JSONPlaceholderClient.class); 43 | 44 | jsonApiClient.getPostById("1").thenAccept(System.out::println); 45 | } 46 | 47 | private void deletePost() { 48 | System.out.println("------ delete a post ------"); 49 | 50 | Response postDeletionResult = jsonPlaceholderClient.deletePostById("42"); 51 | 52 | System.out.println(postDeletionResult.readEntity(JsonObject.class)); 53 | } 54 | 55 | private void updateExistingPost() { 56 | System.out.println("------ update a post ------"); 57 | 58 | JsonObject postUpdate = Json.createObjectBuilder() 59 | .add("id", 42) 60 | .add("title", "Jakarta EE 8") 61 | .add("body", "Work with Jakarta EE 8") 62 | .add("userId", 1) 63 | .build(); 64 | 65 | Response postUpdateResult = jsonPlaceholderClient.updatePostById("42", postUpdate, UUID.randomUUID().toString()); 66 | 67 | System.out.println(postUpdateResult.readEntity(JsonObject.class)); 68 | } 69 | 70 | private void createNewPost() { 71 | System.out.println("------ create new post ------"); 72 | 73 | JsonObject post = Json.createObjectBuilder() 74 | .add("id", 42) 75 | .add("title", "MicroProfile") 76 | .add("body", "Work with MicroProfile") 77 | .add("userId", 1) 78 | .build(); 79 | 80 | Response postCreationResult = jsonPlaceholderClient.createPost(post); 81 | 82 | System.out.println(postCreationResult.readEntity(JsonObject.class)); 83 | } 84 | 85 | private void getSinglePost() { 86 | System.out.println("------ single post ASYNC ------"); 87 | jsonPlaceholderClient.getPostById("1").thenAccept(System.out::println); 88 | } 89 | 90 | private void getAllPosts() { 91 | System.out.println("------ all posts ------"); 92 | 93 | jsonPlaceholderClient.getAllPosts("ASC") 94 | .stream() 95 | .limit(5) 96 | .forEach(System.out::println); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/java/de/rieckpil/blog/ResponseLoggingFilter.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import javax.ws.rs.client.ClientRequestContext; 4 | import javax.ws.rs.client.ClientResponseContext; 5 | import javax.ws.rs.client.ClientResponseFilter; 6 | import java.io.IOException; 7 | 8 | public class ResponseLoggingFilter implements ClientResponseFilter { 9 | 10 | @Override 11 | public void filter(ClientRequestContext clientRequestContext, ClientResponseContext clientResponseContext) throws IOException { 12 | System.out.println("Status code is: " + clientResponseContext.getStatus()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/resources/META-INF/microprofile-config.properties: -------------------------------------------------------------------------------- 1 | de.rieckpil.blog.JSONPlaceholderClient/mp-rest/url=https://jsonplaceholder.typicode.com 2 | de.rieckpil.blog.JSONPlaceholderClient/mp-rest/connectTimeout=3000 3 | de.rieckpil.blog.JSONPlaceholderClient/mp-rest/readTimeout=3000 4 | secrets.value=FOO 5 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/webapp/WEB-INF/beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/webapp/WEB-INF/ibm-web-ext.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/main/webapp/WEB-INF/payara-web.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | / 5 | 6 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/test/java/de/rieckpil/blog/ApplicationIT.java: -------------------------------------------------------------------------------- 1 | package de.rieckpil.blog; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.microshed.testing.jupiter.MicroShedTest; 5 | import org.microshed.testing.testcontainers.ApplicationContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | 8 | @MicroShedTest 9 | public class ApplicationIT { 10 | 11 | @Container 12 | public static ApplicationContainer app = new ApplicationContainer() 13 | .withAppContextRoot("/") 14 | .withReadinessPath("/health/ready"); 15 | 16 | @Test 17 | public void testApplicationStarts() { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /microprofile-rest-client/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO, stdout 2 | log4j.appender=org.apache.log4j.ConsoleAppender 3 | log4j.appender.layout=org.apache.log4j.PatternLayout 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=%r %p %c %x - %m%n 7 | log4j.logger.org.microshed.testing=DEBUG 8 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 4.0.0 6 | de.rieckpil.blog 7 | microprofile-course-parent 8 | 1.0-SNAPSHOT 9 | pom 10 | 11 | 12 | 13 | org.eclipse.microprofile 14 | microprofile 15 | ${microprofile.version} 16 | pom 17 | provided 18 | 19 | 20 | org.microshed 21 | microshed-testing-liberty 22 | ${microshed-testing.version} 23 | test 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter 28 | ${junit-jupiter.version} 29 | test 30 | 31 | 32 | org.slf4j 33 | slf4j-log4j12 34 | ${slf4j-log4j12.version} 35 | test 36 | 37 | 38 | 39 | 40 | 11 41 | 3.3 42 | 5.6.0 43 | 0.9 44 | 1.7.29 45 | ${java.version} 46 | ${java.version} 47 | UTF-8 48 | UTF-8 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-war-plugin 56 | 3.2.3 57 | 58 | false 59 | 60 | 61 | 62 | io.openliberty.tools 63 | liberty-maven-plugin 64 | 3.1 65 | 66 | 20.0.0.5 67 | ../server.xml 68 | 69 | 70 | 71 | maven-resources-plugin 72 | 3.1.0 73 | 74 | 75 | copy-resources 76 | validate 77 | 78 | copy-resources 79 | 80 | 81 | ${project.basedir}/target 82 | 83 | 84 | ../ 85 | 86 | server.xml 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | maven-failsafe-plugin 96 | 3.0.0-M3 97 | 98 | 99 | integration-test 100 | 101 | integration-test 102 | 103 | 104 | false 105 | 106 | 107 | 108 | verify 109 | 110 | verify 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /server.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | microProfile-3.3 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------