├── .github ├── dependabot.yml └── workflows │ ├── build-test-projects.yml │ └── dependabot-automerge.yml ├── .gitignore ├── LICENSE ├── README.md ├── chapter-2 ├── README.md └── chapter-2-simple-project │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ ├── Dockerfile.native-distroless │ │ └── Dockerfile.native-micro │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── GreetingResource.java │ │ │ └── GreetingService.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── index.html │ │ └── application.properties │ └── test │ └── java │ └── org │ └── acme │ ├── GreetingResourceTest.java │ ├── GreetingServiceTest.java │ └── NativeGreetingResourceIT.java ├── chapter-3 ├── README.md ├── chapter-3-quarkus-rest-json │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ ├── CustomRuntimeException.java │ │ │ │ └── Fruit.java │ │ │ │ ├── rest │ │ │ │ ├── CustomError.java │ │ │ │ ├── FruitResource.java │ │ │ │ └── GlobalErrorHandler.java │ │ │ │ └── service │ │ │ │ └── FruitService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── rest │ │ └── FruitResourceTest.java │ │ └── service │ │ └── FruitServiceTest.java ├── chapter-3-spring-rest-json │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── Chapter3SpringRestJsonApplication.java │ │ │ │ ├── config │ │ │ │ └── OpenAPIConfig.java │ │ │ │ ├── domain │ │ │ │ ├── CustomRuntimeException.java │ │ │ │ └── Fruit.java │ │ │ │ ├── rest │ │ │ │ ├── CustomError.java │ │ │ │ ├── FruitController.java │ │ │ │ └── GlobalErrorHandler.java │ │ │ │ └── service │ │ │ │ └── FruitService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── Chapter3SpringRestJsonApplicationTests.java │ │ ├── rest │ │ └── FruitControllerTest.java │ │ └── service │ │ └── FruitServiceTest.java └── chapter-3-spring-webflux-rest-json │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── Chapter3SpringWebfluxRestJsonApplication.java │ │ │ ├── config │ │ │ └── OpenAPIConfig.java │ │ │ ├── domain │ │ │ ├── CustomRuntimeException.java │ │ │ └── Fruit.java │ │ │ ├── rest │ │ │ ├── CustomError.java │ │ │ ├── FruitController.java │ │ │ └── GlobalErrorHandler.java │ │ │ └── service │ │ │ └── FruitService.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── org │ └── acme │ ├── Chapter3SpringWebfluxRestJsonApplicationTests.java │ ├── rest │ └── FruitControllerTest.java │ └── service │ └── FruitServiceTest.java ├── chapter-4 ├── README.md ├── chapter-4-quarkus-panache-activerecord │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ └── rest │ │ │ │ └── FruitResource.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── domain │ │ │ └── FruitTests.java │ │ │ └── rest │ │ │ └── FruitResourceTests.java │ │ └── resources │ │ └── db │ │ └── schema.sql ├── chapter-4-quarkus-panache-reactive-activerecord │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ └── rest │ │ │ │ └── FruitResource.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── TestTransaction.java │ │ │ ├── domain │ │ │ └── FruitTests.java │ │ │ └── rest │ │ │ └── FruitResourceTests.java │ │ └── resources │ │ └── db │ │ └── schema.sql ├── chapter-4-quarkus-panache-reactive-repository │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ ├── repository │ │ │ │ └── FruitRepository.java │ │ │ │ └── rest │ │ │ │ └── FruitResource.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── TestTransaction.java │ │ │ ├── repository │ │ │ └── FruitRepositoryTests.java │ │ │ └── rest │ │ │ └── FruitResourceTests.java │ │ └── resources │ │ └── db │ │ └── schema.sql ├── chapter-4-quarkus-panache-repository │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ ├── repository │ │ │ │ └── FruitRepository.java │ │ │ │ └── rest │ │ │ │ └── FruitResource.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── repository │ │ │ └── FruitRepositoryTests.java │ │ │ └── rest │ │ │ └── FruitResourceTests.java │ │ └── resources │ │ └── db │ │ └── schema.sql ├── chapter-4-spring-data-jpa │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── Chapter4SpringDataJpaApplication.java │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ ├── repository │ │ │ │ └── FruitRepository.java │ │ │ │ └── rest │ │ │ │ └── FruitController.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── Chapter4SpringDataJpaApplicationTests.java │ │ │ ├── repository │ │ │ └── FruitRepositoryTests.java │ │ │ └── rest │ │ │ └── FruitControllerTests.java │ │ └── resources │ │ ├── application.yml │ │ └── db │ │ └── schema.sql └── chapter-4-spring-data-r2dbc │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── Dockerfile.postgres │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── Chapter4SpringDataR2dbcApplication.java │ │ │ ├── domain │ │ │ └── Fruit.java │ │ │ ├── repository │ │ │ └── FruitRepository.java │ │ │ └── rest │ │ │ └── FruitController.java │ └── resources │ │ └── application.yml │ └── test │ ├── java │ └── org │ │ └── acme │ │ ├── Chapter4SpringDataR2dbcApplicationTests.java │ │ ├── TestContainerBase.java │ │ ├── TestTransaction.java │ │ ├── repository │ │ └── FruitRepositoryTests.java │ │ └── rest │ │ └── FruitControllerTests.java │ └── resources │ └── db │ └── schema.sql ├── chapter-5 ├── README.md ├── chapter-5-quarkus-cloud-events │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── func.yaml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ ├── Input.java │ │ │ │ └── Output.java │ │ │ │ └── function │ │ │ │ └── ToUppercaseFunction.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ └── function │ │ ├── ToUppercaseFunctionHttpTests.java │ │ └── ToUppercaseFunctionTest.java ├── chapter-5-quarkus-kafka-streams │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── create-topics.sh │ ├── docker-compose.yaml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── rest │ │ │ │ └── PriceResource.java │ │ │ │ └── service │ │ │ │ ├── PriceConverter.java │ │ │ │ └── PriceGenerator.java │ │ └── resources │ │ │ ├── META-INF │ │ │ └── resources │ │ │ │ └── prices.html │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── rest │ │ └── PriceResourceTest.java │ │ └── service │ │ ├── PriceConverterTests.java │ │ └── PriceGeneratorTests.java ├── chapter-5-quarkus-vertx-eventbus │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── GreetingResource.java │ │ │ │ └── GreetingService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ └── GreetingResourceTest.java ├── chapter-5-spring-cloud-events │ ├── .editorconfig │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── func.yaml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── acme │ │ │ ├── SpringCloudEventsApplication.java │ │ │ ├── domain │ │ │ ├── Input.java │ │ │ └── Output.java │ │ │ └── function │ │ │ └── ToUppercaseFunction.java │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ └── function │ │ ├── ToUppercaseFunctionHttpTests.java │ │ └── ToUppercaseFunctionTests.java ├── chapter-5-spring-eventbus │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── acme │ │ │ └── chapter5springeventbus │ │ │ ├── Chapter5SpringEventbusApplication.java │ │ │ ├── config │ │ │ └── ChannelConfig.java │ │ │ ├── rest │ │ │ └── GreetingController.java │ │ │ └── service │ │ │ ├── GreetingGateway.java │ │ │ └── GreetingService.java │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ └── chapter5springeventbus │ │ ├── Chapter5SpringEventbusApplicationTests.java │ │ ├── rest │ │ └── GreetingControllerTests.java │ │ └── service │ │ └── GreetingServiceTests.java └── chapter-5-spring-kafka-streams │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── README.md │ ├── create-topics.sh │ ├── docker-compose.yaml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── Chapter5SpringKafkaStreamsApplication.java │ │ │ ├── config │ │ │ └── InMemoryChannelConfig.java │ │ │ ├── rest │ │ │ └── PriceController.java │ │ │ └── service │ │ │ ├── InMemoryChannel.java │ │ │ ├── PriceConverter.java │ │ │ └── PriceGenerator.java │ └── resources │ │ ├── application.properties │ │ └── static │ │ └── prices.html │ └── test │ ├── java │ └── org │ │ └── acme │ │ ├── Chapter5SpringKafkaStreamsApplicationTests.java │ │ ├── DockerComposeBase.java │ │ ├── rest │ │ └── PriceControllerTests.java │ │ └── service │ │ ├── PriceConverterTests.java │ │ └── PriceGeneratorTests.java │ └── resources │ └── logback-test.xml ├── chapter-6 ├── README.md ├── chapter-6-quarkus-rest-cloud-config │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── GreeterResource.java │ │ │ │ └── GreetingProperties.java │ │ ├── k8s │ │ │ └── helm.yml │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── GreeterResourceTest.java │ │ └── NativeGreeterResourceIT.java ├── chapter-6-quarkus-rest-config │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── configs │ │ │ └── greeting-env │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── GreeterResource.java │ │ │ │ └── GreetingProperties.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── GreeterResourceTest.java │ │ └── NativeGreeterResourceIT.java ├── chapter-6-quarkus-rest-database │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── k8s │ │ ├── postgresql.yaml │ │ └── service-binding-secret.yaml │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── domain │ │ │ │ └── Fruit.java │ │ │ │ ├── repository │ │ │ │ └── FruitRepository.java │ │ │ │ └── rest │ │ │ │ └── FruitResource.java │ │ └── resources │ │ │ ├── META-INF │ │ │ └── resources │ │ │ │ └── index.html │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ └── rest │ │ └── FruitResourceTests.java ├── chapter-6-quarkus-rest-debug │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── img │ │ ├── add-breakpoint.png │ │ ├── debug-it.png │ │ ├── launch-it.png │ │ └── remote-debugger.png │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ └── GreeterResource.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── GreeterResourceTest.java │ │ └── NativeGreeterResourceIT.java ├── chapter-6-quarkus-rest-monitoring │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── docker │ │ │ ├── Dockerfile.jvm │ │ │ ├── Dockerfile.legacy-jar │ │ │ ├── Dockerfile.native │ │ │ ├── Dockerfile.native-distroless │ │ │ └── Dockerfile.native-micro │ │ ├── java │ │ │ └── org │ │ │ │ └── acme │ │ │ │ ├── FrancophoneService.java │ │ │ │ ├── GreetingResource.java │ │ │ │ ├── MonitoringResource.java │ │ │ │ └── TracedResource.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── org │ │ └── acme │ │ ├── GreetingResourceTest.java │ │ ├── MonitoringResourceTest.java │ │ └── TracedResourceTest.java └── chapter-6-quarkus-rest │ ├── .dockerignore │ ├── .gitignore │ ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties │ ├── README.md │ ├── mvnw │ ├── mvnw.cmd │ ├── pom.xml │ └── src │ ├── main │ ├── docker │ │ ├── Dockerfile.jvm │ │ ├── Dockerfile.legacy-jar │ │ ├── Dockerfile.native │ │ ├── Dockerfile.native-distroless │ │ └── Dockerfile.native-micro │ ├── java │ │ └── org │ │ │ └── acme │ │ │ ├── domain │ │ │ ├── CustomRuntimeException.java │ │ │ └── Fruit.java │ │ │ ├── rest │ │ │ ├── CustomError.java │ │ │ ├── FruitResource.java │ │ │ └── GlobalErrorHandler.java │ │ │ └── service │ │ │ ├── FruitService.java │ │ │ └── SimpleHealthCheck.java │ └── resources │ │ ├── META-INF │ │ └── resources │ │ │ └── index.html │ │ └── application.properties │ └── test │ └── java │ └── org │ └── acme │ ├── rest │ └── FruitResourceTest.java │ └── service │ └── FruitServiceTest.java ├── codeready-workspaces └── Dockerfile.crw-mandrel ├── devfile.yaml └── pom.xml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: / 5 | open-pull-requests-limit: 20 6 | schedule: 7 | interval: daily 8 | ignore: 9 | - dependency-name: org.apache.maven.plugins:maven-compiler-plugin 10 | labels: 11 | - "version-upgrade" 12 | 13 | # Maintain dependencies for GitHub Actions 14 | - package-ecosystem: github-actions 15 | directory: / 16 | open-pull-requests-limit: 10 17 | schedule: 18 | interval: daily 19 | labels: 20 | - "version-upgrade" 21 | 22 | #################################### 23 | # 3.x upgrade branch 24 | - package-ecosystem: maven 25 | directory: / 26 | open-pull-requests-limit: 20 27 | target-branch: 3.x-upgrade 28 | schedule: 29 | interval: daily 30 | labels: 31 | - "version-upgrade" 32 | 33 | # Maintain dependencies for GitHub Actions 34 | - package-ecosystem: github-actions 35 | directory: / 36 | open-pull-requests-limit: 10 37 | target-branch: 3.x-upgrade 38 | schedule: 39 | interval: daily 40 | labels: 41 | - "version-upgrade" 42 | -------------------------------------------------------------------------------- /.github/workflows/dependabot-automerge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - 3.x-upgrade 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | jobs: 14 | dependabot: 15 | runs-on: ubuntu-latest 16 | if: github.actor == 'dependabot[bot]' 17 | steps: 18 | - name: Dependabot metadata 19 | id: metadata 20 | uses: dependabot/fetch-metadata@v2 21 | with: 22 | github-token: "${{ secrets.GITHUB_TOKEN }}" 23 | 24 | - name: Enable auto-merge for Dependabot PRs 25 | if: (steps.metadata.outputs.update-type == 'version-update:semver-patch') || (steps.metadata.outputs.update-type == 'version-update:semver-minor') || (steps.metadata.outputs.update-type == 'version-update:semver-major') 26 | env: 27 | PR_URL: ${{ github.event.pull_request.html_url }} 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | run: gh pr merge --auto --squash "$PR_URL" 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | .jpb 41 | -------------------------------------------------------------------------------- /chapter-2/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 2 - Getting Started with Quarkus 2 | This directory contains example projects used in Chapter 2 - Getting Started with Quarkus. The example projects represent a simple RESTful application. 3 | 4 | ## Examples List 5 | - [Simple Quarkus Project](chapter-2-simple-project/) -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-2/chapter-2-simple-project/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-2-simple-project . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-2-simple-project 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-3-quarkus-rest-json . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-3-quarkus-rest-json 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-2-simple-project . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-2-simple-project 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/java/org/acme/GreetingResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("/hello-resteasy") 9 | public class GreetingResource { 10 | 11 | private final GreetingService greetingService; 12 | 13 | public GreetingResource(GreetingService greetingService) { 14 | this.greetingService = greetingService; 15 | } 16 | 17 | @GET 18 | @Produces(MediaType.TEXT_PLAIN) 19 | public String hello() { 20 | return greetingService.getGreeting(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/java/org/acme/GreetingService.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | 5 | import org.eclipse.microprofile.config.inject.ConfigProperty; 6 | 7 | @ApplicationScoped 8 | public class GreetingService { 9 | private final String greeting; 10 | 11 | public GreetingService(@ConfigProperty(name = "greeting.name") String greeting) { 12 | this.greeting = greeting; 13 | } 14 | 15 | public String getGreeting() { 16 | return "Hello " + this.greeting; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | greeting.name=Quarkus (properties) 2 | %dev.greeting.name=Quarkus for Spring Developer (dev) 3 | %prod.greeting.name=Quarkus for Spring Developer (prod) 4 | %test.greeting.name=RESTEasy 5 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/test/java/org/acme/GreetingResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.is; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | 9 | import io.quarkus.test.junit.DisabledOnNativeImage; 10 | import io.quarkus.test.junit.QuarkusTest; 11 | import io.quarkus.test.junit.mockito.InjectMock; 12 | 13 | @QuarkusTest 14 | public class GreetingResourceTest { 15 | @InjectMock 16 | GreetingService greetingService; 17 | 18 | @Test 19 | @DisabledOnNativeImage 20 | public void testHelloEndpoint() { 21 | Mockito.when(this.greetingService.getGreeting()).thenReturn("Hello Quarkus"); 22 | 23 | given() 24 | .when().get("/hello-resteasy") 25 | .then() 26 | .statusCode(200) 27 | .body(is("Hello Quarkus")); 28 | 29 | Mockito.verify(this.greetingService).getGreeting(); 30 | Mockito.verifyNoMoreInteractions(this.greetingService); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/test/java/org/acme/GreetingServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class GreetingServiceTest { 7 | @Test 8 | void getGreetingOk() { 9 | Assertions.assertEquals("Hello Quarkus", new GreetingService("Quarkus").getGreeting()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-2/chapter-2-simple-project/src/test/java/org/acme/NativeGreetingResourceIT.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.is; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import io.quarkus.test.junit.NativeImageTest; 9 | 10 | @NativeImageTest 11 | public class NativeGreetingResourceIT extends GreetingResourceTest { 12 | @Test 13 | public void testHelloEndpointNative() { 14 | given() 15 | .when().get("/hello-resteasy") 16 | .then() 17 | .statusCode(200) 18 | .body(is("Hello Quarkus for Spring Developer (prod)")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-3/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 3 - Building RESTful Applications 2 | This directory contains example projects used in Chapter 3 - Building RESTful Applications. 3 | 4 | All of the examples showcase the exact same use cases as discussed in Chapter 3 of the book. The examples consist of a RESTful interface exposing several endpoints and server-sent events as well as some error handling. 5 | 6 | ## Examples List 7 | - [Spring MVC RESTful application](chapter-3-spring-rest-json/) 8 | - [Spring WebFlux RESTful application](chapter-3-spring-webflux-rest-json/) 9 | - [Quarkus JAX-RS RESTful application](chapter-3-quarkus-rest-json/) -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-3/chapter-3-quarkus-rest-json/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-3-quarkus-rest-json . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-3-quarkus-rest-json 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-3-quarkus-rest-json . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-3-quarkus-rest-json 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-3-quarkus-rest-json . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-3-quarkus-rest-json 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/java/org/acme/domain/CustomRuntimeException.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class CustomRuntimeException extends RuntimeException { 4 | public CustomRuntimeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/java/org/acme/domain/Fruit.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | public class Fruit { 6 | private String name; 7 | private String description; 8 | 9 | public Fruit(String name, String description) { 10 | this.name = name; 11 | this.description = description; 12 | } 13 | 14 | public Fruit() { 15 | } 16 | 17 | @NotBlank 18 | public String getName() { 19 | return this.name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getDescription() { 27 | return this.description; 28 | } 29 | 30 | public void setDescription(String description) { 31 | this.description = description; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/java/org/acme/rest/CustomError.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.StringJoiner; 4 | 5 | import io.quarkus.runtime.annotations.RegisterForReflection; 6 | 7 | @RegisterForReflection 8 | public class CustomError { 9 | private int errorCode; 10 | private String errorMessage; 11 | 12 | public CustomError() { 13 | } 14 | 15 | public CustomError(int errorCode, String errorMessage) { 16 | this.errorCode = errorCode; 17 | this.errorMessage = errorMessage; 18 | } 19 | 20 | public int getErrorCode() { 21 | return this.errorCode; 22 | } 23 | 24 | public void setErrorCode(int errorCode) { 25 | this.errorCode = errorCode; 26 | } 27 | 28 | public String getErrorMessage() { 29 | return this.errorMessage; 30 | } 31 | 32 | public void setErrorMessage(String errorMessage) { 33 | this.errorMessage = errorMessage; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return new StringJoiner(", ", CustomError.class.getSimpleName() + "[", "]") 39 | .add("errorCode=" + this.errorCode) 40 | .add("errorMessage='" + this.errorMessage + "'") 41 | .toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/java/org/acme/rest/GlobalErrorHandler.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import javax.ws.rs.core.Response; 4 | 5 | import org.acme.domain.CustomRuntimeException; 6 | import org.jboss.resteasy.reactive.server.ServerExceptionMapper; 7 | 8 | public class GlobalErrorHandler { 9 | @ServerExceptionMapper(CustomRuntimeException.class) 10 | public Response handleCustomRuntimeException(CustomRuntimeException cre) { 11 | return Response.serverError() 12 | .header("X-CUSTOM-ERROR", "500") 13 | .entity(new CustomError(500, cre.getMessage())) 14 | .build(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.smallrye-openapi.info-title=Fruits API 2 | quarkus.smallrye-openapi.info-version=1.0.0 3 | quarkus.smallrye-openapi.info-description=Example Fruit Service - Quarkus 4 | quarkus.smallrye-openapi.info-contact-email=edeandrea@redhat.com 5 | quarkus.smallrye-openapi.info-contact-name=Eric Deandrea 6 | quarkus.smallrye-openapi.info-contact-url=https://developers.redhat.com/blog/author/edeandrea 7 | quarkus.smallrye-openapi.info-license-name=Apache 2.0 8 | quarkus.smallrye-openapi.info-license-url=https://www.apache.org/licenses/LICENSE-2.0.html 9 | quarkus.smallrye-openapi.operation-id-strategy=METHOD 10 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-quarkus-rest-json/src/test/java/org/acme/service/FruitServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.acme.domain.Fruit; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; 9 | 10 | class FruitServiceTest { 11 | FruitService fruitService = new FruitService(); 12 | 13 | @Test 14 | public void getFruits() { 15 | assertThat(this.fruitService.getFruits()) 16 | .hasSize(2); 17 | } 18 | 19 | @Test 20 | public void getFruitFound() { 21 | var fruit = this.fruitService.getFruit("Apple") 22 | .subscribe() 23 | .withSubscriber(UniAssertSubscriber.create()) 24 | .assertCompleted() 25 | .getItem(); 26 | 27 | assertThat(fruit) 28 | .isNotNull() 29 | .extracting(Fruit::getName, Fruit::getDescription) 30 | .containsExactly("Apple", "Winter fruit"); 31 | } 32 | 33 | @Test 34 | public void getFruitNotFound() { 35 | this.fruitService.getFruit("Pear") 36 | .subscribe() 37 | .withSubscriber(UniAssertSubscriber.create()) 38 | .assertCompleted() 39 | .assertItem(null); 40 | } 41 | 42 | @Test 43 | public void addFruit() { 44 | assertThat(this.fruitService.addFruit(new Fruit("Pear", "Delicious fruit"))) 45 | .hasSize(3); 46 | } 47 | 48 | @Test 49 | public void delete() { 50 | this.fruitService.deleteFruit("Apple"); 51 | 52 | assertThat(this.fruitService.getFruits()) 53 | .hasSize(1); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-3/chapter-3-spring-rest-json/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 3 - Spring MVC RESTful application 2 | This project contains a simple Spring MVC RESTful application. -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/Chapter3SpringRestJsonApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter3SpringRestJsonApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter3SpringRestJsonApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/config/OpenAPIConfig.java: -------------------------------------------------------------------------------- 1 | package org.acme.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import io.swagger.v3.oas.models.OpenAPI; 7 | import io.swagger.v3.oas.models.info.Contact; 8 | import io.swagger.v3.oas.models.info.Info; 9 | import io.swagger.v3.oas.models.info.License; 10 | 11 | @Configuration 12 | public class OpenAPIConfig { 13 | @Bean 14 | public OpenAPI openApi() { 15 | return new OpenAPI() 16 | .info(getInfo()); 17 | } 18 | 19 | private Info getInfo() { 20 | return new Info() 21 | .title("Fruits API") 22 | .version("1.0.0") 23 | .description("Example Fruit Service - Spring MVC") 24 | .license( 25 | new License() 26 | .name("Apache 2.0") 27 | .url("https://www.apache.org/licenses/LICENSE-2.0.html") 28 | ) 29 | .contact( 30 | new Contact() 31 | .name("Eric Deandrea") 32 | .email("edeandrea@redhat.com") 33 | .url("https://developers.redhat.com/blog/author/edeandrea") 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/domain/CustomRuntimeException.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class CustomRuntimeException extends RuntimeException { 4 | public CustomRuntimeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/domain/Fruit.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | public class Fruit { 6 | private String name; 7 | private String description; 8 | 9 | public Fruit(String name, String description) { 10 | this.name = name; 11 | this.description = description; 12 | } 13 | 14 | public Fruit() { 15 | } 16 | 17 | @NotBlank 18 | public String getName() { 19 | return this.name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getDescription() { 27 | return this.description; 28 | } 29 | 30 | public void setDescription(String description) { 31 | this.description = description; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/rest/CustomError.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.StringJoiner; 4 | 5 | public class CustomError { 6 | private int errorCode; 7 | private String errorMessage; 8 | 9 | public CustomError() { 10 | } 11 | 12 | public CustomError(int errorCode, String errorMessage) { 13 | this.errorCode = errorCode; 14 | this.errorMessage = errorMessage; 15 | } 16 | 17 | public int getErrorCode() { 18 | return this.errorCode; 19 | } 20 | 21 | public void setErrorCode(int errorCode) { 22 | this.errorCode = errorCode; 23 | } 24 | 25 | public String getErrorMessage() { 26 | return this.errorMessage; 27 | } 28 | 29 | public void setErrorMessage(String errorMessage) { 30 | this.errorMessage = errorMessage; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return new StringJoiner(", ", CustomError.class.getSimpleName() + "[", "]") 36 | .add("errorCode=" + this.errorCode) 37 | .add("errorMessage='" + this.errorMessage + "'") 38 | .toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/rest/GlobalErrorHandler.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import org.acme.domain.CustomRuntimeException; 4 | 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | @RestControllerAdvice 11 | public class GlobalErrorHandler { 12 | @ExceptionHandler(CustomRuntimeException.class) 13 | public ResponseEntity handleCustomRuntimeException(CustomRuntimeException cre) { 14 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) 15 | .header("X-CUSTOM-ERROR", "500") 16 | .body(new CustomError(500, cre.getMessage())); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/java/org/acme/service/FruitService.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.util.Collection; 4 | import java.util.Optional; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import java.util.concurrent.ConcurrentMap; 7 | 8 | import org.acme.domain.CustomRuntimeException; 9 | import org.acme.domain.Fruit; 10 | 11 | import org.springframework.stereotype.Service; 12 | 13 | @Service 14 | public class FruitService { 15 | private ConcurrentMap fruits = new ConcurrentHashMap<>(); 16 | 17 | public FruitService() { 18 | this.fruits.put("Apple", new Fruit("Apple", "Winter fruit")); 19 | this.fruits.put("Pineapple", new Fruit("Pineapple", "Tropical fruit")); 20 | } 21 | 22 | public Collection getFruits() { 23 | return this.fruits.values(); 24 | } 25 | 26 | public Optional getFruit(String fruitName) { 27 | return Optional.ofNullable(this.fruits.get(fruitName)); 28 | } 29 | 30 | public Collection addFruit(Fruit fruit) { 31 | this.fruits.put(fruit.getName(), fruit); 32 | return this.fruits.values(); 33 | } 34 | 35 | public void deleteFruit(String fruitName) { 36 | this.fruits.remove(fruitName); 37 | } 38 | 39 | public void performWorkGeneratingError() { 40 | throw new CustomRuntimeException("Got some kind of error from somewhere"); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/test/java/org/acme/Chapter3SpringRestJsonApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class Chapter3SpringRestJsonApplicationTests { 9 | 10 | @Test 11 | void contextLoads() { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-rest-json/src/test/java/org/acme/service/FruitServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.acme.domain.Fruit; 6 | import org.junit.jupiter.api.Test; 7 | 8 | class FruitServiceTest { 9 | FruitService fruitService = new FruitService(); 10 | 11 | @Test 12 | public void getFruits() { 13 | assertThat(this.fruitService.getFruits()) 14 | .hasSize(2); 15 | } 16 | 17 | @Test 18 | public void getFruitFound() { 19 | assertThat(this.fruitService.getFruit("Apple")) 20 | .isPresent() 21 | .get() 22 | .extracting(Fruit::getName, Fruit::getDescription) 23 | .containsExactly("Apple", "Winter fruit"); 24 | } 25 | 26 | @Test 27 | public void getFruitNotFound() { 28 | assertThat(this.fruitService.getFruit("Pear")) 29 | .isNotPresent(); 30 | } 31 | 32 | @Test 33 | public void addFruit() { 34 | assertThat(this.fruitService.addFruit(new Fruit("Pear", "Delicious fruit"))) 35 | .hasSize(3); 36 | } 37 | 38 | @Test 39 | public void delete() { 40 | this.fruitService.deleteFruit("Apple"); 41 | 42 | assertThat(this.fruitService.getFruits()) 43 | .hasSize(1); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-3/chapter-3-spring-webflux-rest-json/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 3 - Spring WebFlux RESTful application 2 | This project contains a simple Spring WebFlux RESTful application. -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/Chapter3SpringWebfluxRestJsonApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter3SpringWebfluxRestJsonApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter3SpringWebfluxRestJsonApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/config/OpenAPIConfig.java: -------------------------------------------------------------------------------- 1 | package org.acme.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import io.swagger.v3.oas.models.OpenAPI; 7 | import io.swagger.v3.oas.models.info.Contact; 8 | import io.swagger.v3.oas.models.info.Info; 9 | import io.swagger.v3.oas.models.info.License; 10 | 11 | @Configuration 12 | public class OpenAPIConfig { 13 | @Bean 14 | public OpenAPI openApi() { 15 | return new OpenAPI() 16 | .info(getInfo()); 17 | } 18 | 19 | private Info getInfo() { 20 | return new Info() 21 | .title("Fruits API") 22 | .version("1.0.0") 23 | .description("Example Fruit Service - Spring WebFlux") 24 | .license( 25 | new License() 26 | .name("Apache 2.0") 27 | .url("https://www.apache.org/licenses/LICENSE-2.0.html") 28 | ) 29 | .contact( 30 | new Contact() 31 | .name("Eric Deandrea") 32 | .email("edeandrea@redhat.com") 33 | .url("https://developers.redhat.com/blog/author/edeandrea") 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/domain/CustomRuntimeException.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class CustomRuntimeException extends RuntimeException { 4 | public CustomRuntimeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/domain/Fruit.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | public class Fruit { 6 | private String name; 7 | private String description; 8 | 9 | public Fruit(String name, String description) { 10 | this.name = name; 11 | this.description = description; 12 | } 13 | 14 | public Fruit() { 15 | } 16 | 17 | @NotBlank 18 | public String getName() { 19 | return this.name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getDescription() { 27 | return this.description; 28 | } 29 | 30 | public void setDescription(String description) { 31 | this.description = description; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/rest/CustomError.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.StringJoiner; 4 | 5 | public class CustomError { 6 | private int errorCode; 7 | private String errorMessage; 8 | 9 | public CustomError() { 10 | } 11 | 12 | public CustomError(int errorCode, String errorMessage) { 13 | this.errorCode = errorCode; 14 | this.errorMessage = errorMessage; 15 | } 16 | 17 | public int getErrorCode() { 18 | return this.errorCode; 19 | } 20 | 21 | public void setErrorCode(int errorCode) { 22 | this.errorCode = errorCode; 23 | } 24 | 25 | public String getErrorMessage() { 26 | return this.errorMessage; 27 | } 28 | 29 | public void setErrorMessage(String errorMessage) { 30 | this.errorMessage = errorMessage; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return new StringJoiner(", ", CustomError.class.getSimpleName() + "[", "]") 36 | .add("errorCode=" + this.errorCode) 37 | .add("errorMessage='" + this.errorMessage + "'") 38 | .toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/java/org/acme/rest/GlobalErrorHandler.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import org.acme.domain.CustomRuntimeException; 4 | 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.ExceptionHandler; 8 | import org.springframework.web.bind.annotation.RestControllerAdvice; 9 | 10 | import reactor.core.publisher.Mono; 11 | 12 | @RestControllerAdvice 13 | public class GlobalErrorHandler { 14 | @ExceptionHandler(CustomRuntimeException.class) 15 | public Mono> handleCustomRuntimeException(CustomRuntimeException cre) { 16 | return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) 17 | .header("X-CUSTOM-ERROR", "500") 18 | .body(new CustomError(500, cre.getMessage()))); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/test/java/org/acme/Chapter3SpringWebfluxRestJsonApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class Chapter3SpringWebfluxRestJsonApplicationTests { 9 | 10 | @Test 11 | void contextLoads() { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter-3/chapter-3-spring-webflux-rest-json/src/test/java/org/acme/service/FruitServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import org.acme.domain.Fruit; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import reactor.test.StepVerifier; 7 | 8 | class FruitServiceTest { 9 | FruitService fruitService = new FruitService(); 10 | 11 | @Test 12 | public void getFruits() { 13 | StepVerifier.create(this.fruitService.getFruits()) 14 | .expectNextCount(2) 15 | .verifyComplete(); 16 | } 17 | 18 | @Test 19 | public void getFruitFound() { 20 | StepVerifier.create(this.fruitService.getFruit("Apple")) 21 | .expectNextMatches(fruit -> "Apple".equalsIgnoreCase(fruit.getName()) && "Winter fruit".equalsIgnoreCase(fruit.getDescription())) 22 | .verifyComplete(); 23 | } 24 | 25 | @Test 26 | public void getFruitNotFound() { 27 | StepVerifier.create(this.fruitService.getFruit("Pear")) 28 | .verifyComplete(); 29 | } 30 | 31 | @Test 32 | public void addFruit() { 33 | StepVerifier.create(this.fruitService.addFruit(new Fruit("Pear", "Delicious fruit"))) 34 | .expectNextCount(3) 35 | .verifyComplete(); 36 | } 37 | 38 | @Test 39 | public void delete() { 40 | StepVerifier.create(this.fruitService.deleteFruit("Apple")) 41 | .verifyComplete(); 42 | 43 | StepVerifier.create(this.fruitService.getFruits()) 44 | .expectNextCount(1) 45 | .verifyComplete(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chapter-4/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 4 - Persistence 2 | This directory contains example projects used in 4 - Persistence. 3 | 4 | All of the examples showcase the exact same use cases as discussed in Chapter 4 of the book. The examples consist of a RESTful interface exposing several endpoints using a persistent back-end communicating with a PostgreSQL database. 5 | 6 | ## Examples List 7 | - [Spring Data JPA](chapter-4-spring-data-jpa/) 8 | - [Quarkus Panache (Repository Pattern)](chapter-4-quarkus-panache-repository/) 9 | - [Quarkus Panache (Active Record Pattern)](chapter-4-quarkus-panache-activerecord/) 10 | - [Spring Data R2DBC](chapter-4-spring-data-r2dbc/) 11 | - [Quarkus Panache Reactive (Repository Pattern)](chapter-4-quarkus-panache-reactive-repository/) 12 | - [Quarkus Panache Reactive (Active Record Pattern)](chapter-4-quarkus-panache-reactive-activerecord/) -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-quarkus-panache-activerecord/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/main/java/org/acme/rest/FruitResource.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.List; 4 | 5 | import javax.transaction.Transactional; 6 | import javax.validation.Valid; 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.POST; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.PathParam; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.core.MediaType; 14 | import javax.ws.rs.core.Response; 15 | import javax.ws.rs.core.Response.Status; 16 | 17 | import org.acme.domain.Fruit; 18 | 19 | import io.smallrye.common.annotation.Blocking; 20 | 21 | @Path("/fruits") 22 | @Blocking 23 | public class FruitResource { 24 | @GET 25 | @Produces(MediaType.APPLICATION_JSON) 26 | public List getAll() { 27 | return Fruit.listAll(); 28 | } 29 | 30 | @GET 31 | @Path("/{name}") 32 | @Produces(MediaType.APPLICATION_JSON) 33 | public Response getFruit(@PathParam("name") String name) { 34 | return Fruit.findByName(name) 35 | .map(fruit -> Response.ok(fruit).build()) 36 | .orElseGet(() -> Response.status(Status.NOT_FOUND).build()); 37 | } 38 | 39 | @POST 40 | @Produces(MediaType.APPLICATION_JSON) 41 | @Consumes(MediaType.APPLICATION_JSON) 42 | @Transactional 43 | public Fruit addFruit(@Valid Fruit fruit) { 44 | Fruit.persist(fruit); 45 | return fruit; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | quarkus: 2 | datasource: 3 | username: fruits 4 | password: fruits 5 | devservices: 6 | image-name: quay.io/edeandrea/postgres-13-fruits:latest 7 | hibernate-orm: 8 | database: 9 | generation: validate 10 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/test/java/org/acme/domain/FruitTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Optional; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import io.quarkus.test.TestTransaction; 10 | import io.quarkus.test.junit.QuarkusTest; 11 | 12 | @QuarkusTest 13 | @TestTransaction 14 | class FruitTests { 15 | @Test 16 | public void findByName() { 17 | Fruit.persist(new Fruit(null, "Grapefruit", "Summer fruit")); 18 | 19 | Optional fruit = Fruit.findByName("Grapefruit"); 20 | assertThat(fruit) 21 | .isNotNull() 22 | .isPresent() 23 | .get() 24 | .extracting("name", "description") 25 | .containsExactly("Grapefruit", "Summer fruit"); 26 | 27 | assertThat(fruit.get().id) 28 | .isNotNull() 29 | .isGreaterThan(2L); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-activerecord/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-quarkus-panache-reactive-activerecord/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | ### 15 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 16 | WORKDIR /work/ 17 | RUN chown 1001 /work \ 18 | && chmod "g+rwX" /work \ 19 | && chown 1001:root /work 20 | COPY --chown=1001:root target/*-runner /work/application 21 | 22 | EXPOSE 8080 23 | USER 1001 24 | 25 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 14 | # 15 | # Then run the container using: 16 | # 17 | ### 18 | FROM quay.io/quarkus/quarkus-micro-image:1.0 19 | WORKDIR /work/ 20 | RUN chown 1001 /work \ 21 | && chmod "g+rwX" /work \ 22 | && chown 1001:root /work 23 | COPY --chown=1001:root target/*-runner /work/application 24 | 25 | EXPOSE 8080 26 | USER 1001 27 | 28 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 29 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/main/java/org/acme/rest/FruitResource.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.List; 4 | 5 | import javax.validation.Valid; 6 | import javax.ws.rs.Consumes; 7 | import javax.ws.rs.GET; 8 | import javax.ws.rs.POST; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.PathParam; 11 | import javax.ws.rs.Produces; 12 | import javax.ws.rs.core.MediaType; 13 | import javax.ws.rs.core.Response; 14 | import javax.ws.rs.core.Response.Status; 15 | 16 | import org.acme.domain.Fruit; 17 | 18 | import io.quarkus.hibernate.reactive.panache.common.runtime.ReactiveTransactional; 19 | import io.smallrye.mutiny.Uni; 20 | 21 | @Path("/fruits") 22 | public class FruitResource { 23 | @GET 24 | @Produces(MediaType.APPLICATION_JSON) 25 | public Uni> getAll() { 26 | return Fruit.listAll(); 27 | } 28 | 29 | @GET 30 | @Path("/{name}") 31 | @Produces(MediaType.APPLICATION_JSON) 32 | public Uni getFruit(@PathParam("name") String name) { 33 | return Fruit.findByName(name) 34 | .onItem().ifNotNull().transform(fruit -> Response.ok(fruit).build()) 35 | .onItem().ifNull().continueWith(() -> Response.status(Status.NOT_FOUND).build()); 36 | } 37 | 38 | @POST 39 | @Produces(MediaType.APPLICATION_JSON) 40 | @Consumes(MediaType.APPLICATION_JSON) 41 | @ReactiveTransactional 42 | public Uni addFruit(@Valid Fruit fruit) { 43 | return Fruit.persist(fruit).replaceWith(fruit); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | quarkus: 2 | datasource: 3 | username: fruits 4 | password: fruits 5 | devservices: 6 | image-name: quay.io/edeandrea/postgres-13-fruits:latest 7 | hibernate-orm: 8 | database: 9 | generation: none 10 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/test/java/org/acme/TestTransaction.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import io.quarkus.hibernate.reactive.panache.Panache; 6 | import io.smallrye.mutiny.Uni; 7 | 8 | /** 9 | * For performing transactional rollback within a reactive pipeline 10 | */ 11 | public class TestTransaction { 12 | public static Uni withRollback(Supplier> uni) { 13 | return Panache.getSession() 14 | .flatMap(session -> session.withTransaction(tx -> { 15 | tx.markForRollback(); 16 | return uni.get(); 17 | })); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/test/java/org/acme/domain/FruitTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.time.Duration; 6 | 7 | import org.acme.TestTransaction; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import io.quarkus.test.junit.QuarkusTest; 11 | 12 | @QuarkusTest 13 | public class FruitTests { 14 | @Test 15 | public void findByName() { 16 | Fruit fruit = TestTransaction.withRollback(() -> 17 | Fruit 18 | .persist(new Fruit(null, "Grapefruit", "Summer fruit")) 19 | .replaceWith(Fruit.findByName("Grapefruit")) 20 | ) 21 | .await() 22 | .atMost(Duration.ofSeconds(10)); 23 | 24 | assertThat(fruit) 25 | .isNotNull() 26 | .extracting("name", "description") 27 | .containsExactly("Grapefruit", "Summer fruit"); 28 | 29 | assertThat(fruit.id) 30 | .isNotNull() 31 | .isGreaterThan(2L); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-activerecord/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-quarkus-panache-reactive-repository/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/main/java/org/acme/repository/FruitRepository.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | 5 | import org.acme.domain.Fruit; 6 | 7 | import io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase; 8 | import io.smallrye.mutiny.Uni; 9 | 10 | @ApplicationScoped 11 | public class FruitRepository implements PanacheRepositoryBase { 12 | public Uni findByName(String name) { 13 | return find("name", name).firstResult(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | quarkus: 2 | datasource: 3 | username: fruits 4 | password: fruits 5 | devservices: 6 | image-name: quay.io/edeandrea/postgres-13-fruits:latest 7 | hibernate-orm: 8 | database: 9 | generation: none 10 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/test/java/org/acme/TestTransaction.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import io.quarkus.hibernate.reactive.panache.Panache; 6 | import io.smallrye.mutiny.Uni; 7 | 8 | /** 9 | * For performing transactional rollback within a reactive pipeline 10 | */ 11 | public class TestTransaction { 12 | public static Uni withRollback(Supplier> uni) { 13 | return Panache.getSession() 14 | .flatMap(session -> session.withTransaction(tx -> { 15 | tx.markForRollback(); 16 | return uni.get(); 17 | })); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/test/java/org/acme/repository/FruitRepositoryTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.time.Duration; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.acme.TestTransaction; 10 | import org.acme.domain.Fruit; 11 | import org.junit.jupiter.api.Test; 12 | 13 | import io.quarkus.test.junit.QuarkusTest; 14 | 15 | @QuarkusTest 16 | class FruitRepositoryTests { 17 | @Inject 18 | FruitRepository fruitRepository; 19 | 20 | @Test 21 | public void findByName() { 22 | Fruit fruit = TestTransaction.withRollback(() -> 23 | this.fruitRepository 24 | .persist(new Fruit(null, "Grapefruit", "Summer fruit")) 25 | .replaceWith(this.fruitRepository.findByName("Grapefruit")) 26 | ) 27 | .await() 28 | .atMost(Duration.ofSeconds(10)); 29 | 30 | assertThat(fruit) 31 | .isNotNull() 32 | .extracting(Fruit::getName, Fruit::getDescription) 33 | .containsExactly("Grapefruit", "Summer fruit"); 34 | 35 | assertThat(fruit.getId()) 36 | .isNotNull() 37 | .isGreaterThan(2L); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-reactive-repository/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-quarkus-panache-repository/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-4-quarkus-persistence . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-4-quarkus-persistence . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-4-quarkus-persistence 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/java/org/acme/repository/FruitRepository.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import java.util.Optional; 4 | 5 | import javax.enterprise.context.ApplicationScoped; 6 | 7 | import org.acme.domain.Fruit; 8 | 9 | import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase; 10 | 11 | @ApplicationScoped 12 | public class FruitRepository implements PanacheRepositoryBase { 13 | public Optional findByName(String name) { 14 | return find("name", name).firstResultOptional(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/java/org/acme/rest/FruitResource.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.List; 4 | 5 | import javax.transaction.Transactional; 6 | import javax.validation.Valid; 7 | import javax.ws.rs.Consumes; 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.POST; 10 | import javax.ws.rs.Path; 11 | import javax.ws.rs.PathParam; 12 | import javax.ws.rs.Produces; 13 | import javax.ws.rs.core.MediaType; 14 | import javax.ws.rs.core.Response; 15 | import javax.ws.rs.core.Response.Status; 16 | 17 | import org.acme.domain.Fruit; 18 | import org.acme.repository.FruitRepository; 19 | 20 | import io.smallrye.common.annotation.Blocking; 21 | 22 | @Path("/fruits") 23 | @Blocking 24 | public class FruitResource { 25 | private final FruitRepository fruitRepository; 26 | 27 | public FruitResource(FruitRepository fruitRepository) { 28 | this.fruitRepository = fruitRepository; 29 | } 30 | 31 | @GET 32 | @Produces(MediaType.APPLICATION_JSON) 33 | public List getAll() { 34 | return this.fruitRepository.listAll(); 35 | } 36 | 37 | @GET 38 | @Path("/{name}") 39 | @Produces(MediaType.APPLICATION_JSON) 40 | public Response getFruit(@PathParam("name") String name) { 41 | return this.fruitRepository.findByName(name) 42 | .map(fruit -> Response.ok(fruit).build()) 43 | .orElseGet(() -> Response.status(Status.NOT_FOUND).build()); 44 | } 45 | 46 | @POST 47 | @Produces(MediaType.APPLICATION_JSON) 48 | @Consumes(MediaType.APPLICATION_JSON) 49 | @Transactional 50 | public Fruit addFruit(@Valid Fruit fruit) { 51 | this.fruitRepository.persist(fruit); 52 | return fruit; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | quarkus: 2 | datasource: 3 | username: fruits 4 | password: fruits 5 | devservices: 6 | image-name: quay.io/edeandrea/postgres-13-fruits:latest 7 | hibernate-orm: 8 | database: 9 | generation: validate 10 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/test/java/org/acme/repository/FruitRepositoryTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Optional; 6 | 7 | import javax.inject.Inject; 8 | 9 | import org.acme.domain.Fruit; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import io.quarkus.test.TestTransaction; 13 | import io.quarkus.test.junit.QuarkusTest; 14 | 15 | @QuarkusTest 16 | @TestTransaction 17 | class FruitRepositoryTests { 18 | @Inject 19 | FruitRepository fruitRepository; 20 | 21 | @Test 22 | public void findByName() { 23 | this.fruitRepository.persist(new Fruit(null, "Grapefruit", "Summer fruit")); 24 | 25 | Optional fruit = this.fruitRepository.findByName("Grapefruit"); 26 | assertThat(fruit) 27 | .isNotNull() 28 | .isPresent() 29 | .get() 30 | .extracting(Fruit::getName, Fruit::getDescription) 31 | .containsExactly("Grapefruit", "Summer fruit"); 32 | 33 | assertThat(fruit.get().getId()) 34 | .isNotNull() 35 | .isGreaterThan(2L); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-quarkus-panache-repository/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-spring-data-jpa/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 4 - Spring Data JPA 2 | This is an example using Spring Data JPA. 3 | 4 | ## Start up Database 5 | This application expects a PostgreSQL database running on localhost. You can use Docker to start the database: 6 | 7 | ```shell 8 | docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name chapter4 -p 5432:5432 quay.io/edeandrea/postgres-13-fruits:latest 9 | ``` 10 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/main/java/org/acme/Chapter4SpringDataJpaApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter4SpringDataJpaApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter4SpringDataJpaApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/main/java/org/acme/repository/FruitRepository.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import java.util.Optional; 4 | 5 | import org.acme.domain.Fruit; 6 | 7 | import org.springframework.data.jpa.repository.JpaRepository; 8 | 9 | public interface FruitRepository extends JpaRepository { 10 | Optional findByName(String name); 11 | } 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | username: fruits 4 | password: fruits 5 | url: jdbc:postgresql://localhost:5432/fruits 6 | jpa: 7 | hibernate: 8 | ddl-auto: validate -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/test/java/org/acme/Chapter4SpringDataJpaApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class Chapter4SpringDataJpaApplicationTests { 9 | 10 | @Test 11 | void contextLoads() { 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/test/java/org/acme/repository/FruitRepositoryTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Optional; 6 | 7 | import org.acme.domain.Fruit; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | @SpringBootTest 15 | @Transactional 16 | class FruitRepositoryTests { 17 | @Autowired 18 | FruitRepository fruitRepository; 19 | 20 | @Test 21 | public void findByName() { 22 | this.fruitRepository.save(new Fruit(null, "Grapefruit", "Summer fruit")); 23 | 24 | Optional fruit = this.fruitRepository.findByName("Grapefruit"); 25 | assertThat(fruit) 26 | .isNotNull() 27 | .isPresent() 28 | .get() 29 | .extracting(Fruit::getName, Fruit::getDescription) 30 | .containsExactly("Grapefruit", "Summer fruit"); 31 | 32 | assertThat(fruit.get().getId()) 33 | .isNotNull() 34 | .isGreaterThan(2L); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:tc:postgresql:13:///fruits?TC_INITSCRIPT=db/schema.sql 4 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-jpa/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-4/chapter-4-spring-data-r2dbc/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/Dockerfile.postgres: -------------------------------------------------------------------------------- 1 | FROM postgres:13 2 | 3 | LABEL maintainer="Eric Deandrea edeandrea@redhat.com" 4 | 5 | ENV POSTGRES_USER=fruits \ 6 | POSTGRES_PASSWORD=fruits \ 7 | POSTGRES_DB=fruits \ 8 | INIT_DIR=/docker-entrypoint-initdb.d/ 9 | 10 | RUN mkdir -p $INIT_DIR 11 | COPY src/test/resources/db/ $INIT_DIR 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 4 - Spring Data R2DBC 2 | This is an example using Spring Data R2DBC. 3 | 4 | ## Start up Database 5 | This application expects a PostgreSQL database running on localhost. You can use Docker to start the database: 6 | 7 | ```shell 8 | docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name chapter4 -p 5432:5432 quay.io/edeandrea/postgres-13-fruits:latest 9 | ``` 10 | 11 | ## Start up the Application 12 | The application can be packaged using `./mvnw package`. 13 | 14 | Navigate to the root directory and launch the application: 15 | 16 | ```shell script 17 | cd chapter-4-spring-data-r2dbc 18 | ./mvnw spring-boot:run 19 | ``` 20 | 21 | Try to get fruits by running the following command with `curl`: 22 | 23 | ``` 24 | curl http://localhost:8080/fruits 25 | ``` 26 | You should get two fruits: 27 | ```json 28 | [ 29 | { 30 | "description": "Hearty fruit", 31 | "id": 1, 32 | "name": "Apple" 33 | }, 34 | { 35 | "description": "Juicy fruit", 36 | "id": 2, 37 | "name": "Pear" 38 | } 39 | ] 40 | ``` 41 | You can also add a fruit by running the following command: 42 | 43 | ```shell script 44 | curl --header "Content-Type: application/json" \ 45 | --request POST \ 46 | --data '{"description":"Best fruit ever","name":"Watermelon"}' \ 47 | http://localhost:8080/fruits 48 | ``` -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/main/java/org/acme/Chapter4SpringDataR2dbcApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter4SpringDataR2dbcApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter4SpringDataR2dbcApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/main/java/org/acme/repository/FruitRepository.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import org.acme.domain.Fruit; 4 | 5 | import org.springframework.data.repository.reactive.ReactiveCrudRepository; 6 | 7 | import reactor.core.publisher.Mono; 8 | 9 | public interface FruitRepository extends ReactiveCrudRepository { 10 | Mono findByName(String name); 11 | } 12 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | r2dbc: 3 | username: fruits 4 | password: fruits 5 | url: r2dbc:postgresql://localhost:5432/fruits -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/test/java/org/acme/Chapter4SpringDataR2dbcApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class Chapter4SpringDataR2dbcApplicationTests extends TestContainerBase { 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/test/java/org/acme/TestContainerBase.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.testcontainers.containers.PostgreSQLContainer; 4 | import org.testcontainers.junit.jupiter.Container; 5 | import org.testcontainers.junit.jupiter.Testcontainers; 6 | 7 | import org.springframework.test.annotation.DirtiesContext; 8 | import org.springframework.test.context.DynamicPropertyRegistry; 9 | import org.springframework.test.context.DynamicPropertySource; 10 | 11 | @Testcontainers 12 | @DirtiesContext 13 | public abstract class TestContainerBase { 14 | @Container 15 | static final PostgreSQLContainer DB = new PostgreSQLContainer<>("postgres:13") 16 | .withDatabaseName("fruits") 17 | .withUsername("fruits") 18 | .withPassword("fruits") 19 | .withInitScript("db/schema.sql"); 20 | 21 | @DynamicPropertySource 22 | static void registerDynamicProperties(DynamicPropertyRegistry registry) { 23 | registry.add("spring.r2dbc.url", TestContainerBase::getDbUrl); 24 | registry.add("spring.r2dbc.username", DB::getUsername); 25 | registry.add("spring.r2dbc.password", DB::getPassword); 26 | } 27 | 28 | private static String getDbUrl() { 29 | return String.format("r2dbc:postgresql://%s:%d/%s", DB.getHost(), DB.getFirstMappedPort(), DB.getDatabaseName()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/test/java/org/acme/TestTransaction.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.transaction.reactive.TransactionalOperator; 7 | 8 | import reactor.core.publisher.Mono; 9 | 10 | /** 11 | * For performing transactional rollback within a reactive pipeline 12 | */ 13 | @Component 14 | public class TestTransaction { 15 | private final TransactionalOperator rxtx; 16 | 17 | public TestTransaction(TransactionalOperator rxtx) { 18 | this.rxtx = rxtx; 19 | } 20 | 21 | public Mono withRollback(Supplier> mono) { 22 | return this.rxtx.execute(tx -> { 23 | tx.setRollbackOnly(); 24 | return mono.get(); 25 | }).next(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/test/java/org/acme/repository/FruitRepositoryTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.time.Duration; 6 | 7 | import org.acme.TestContainerBase; 8 | import org.acme.TestTransaction; 9 | import org.acme.domain.Fruit; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | @SpringBootTest 16 | class FruitRepositoryTests extends TestContainerBase { 17 | @Autowired 18 | FruitRepository fruitRepository; 19 | 20 | @Autowired 21 | TestTransaction testTransaction; 22 | 23 | @Test 24 | public void findByName() { 25 | Fruit fruit = this.testTransaction.withRollback(() -> 26 | this.fruitRepository 27 | .save(new Fruit(null, "Grapefruit", "Summer fruit")) 28 | .then(this.fruitRepository.findByName("Grapefruit")) 29 | ) 30 | .block(Duration.ofSeconds(10)); 31 | 32 | assertThat(fruit) 33 | .isNotNull() 34 | .extracting(Fruit::getName, Fruit::getDescription) 35 | .containsExactly("Grapefruit", "Summer fruit"); 36 | 37 | assertThat(fruit.getId()) 38 | .isNotNull() 39 | .isGreaterThan(2L); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chapter-4/chapter-4-spring-data-r2dbc/src/test/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE fruits 2 | ( 3 | id bigserial NOT NULL, 4 | name varchar(255) NOT NULL UNIQUE, 5 | description varchar(255), 6 | PRIMARY KEY (id) 7 | ); 8 | 9 | INSERT INTO fruits(name, description) VALUES 10 | ('Apple', 'Hearty fruit'), 11 | ('Pear', 'Juicy fruit'); 12 | -------------------------------------------------------------------------------- /chapter-5/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 5 - Event Driven Services 2 | This directory contains example projects used in 5 - Event Driven Services. 3 | 4 | All of the examples showcase the exact same use cases as discussed in Chapter 3 of the book. 5 | 6 | ## Examples List 7 | - [Spring EventBus](chapter-5-spring-eventbus/) 8 | - [Quarkus Vertx EventBus](chapter-5-quarkus-vertx-eventbus/) 9 | - [Spring Kafka Streams](chapter-5-spring-kafka-streams/) 10 | - [Quarkus Kafka Streams](chapter-5-quarkus-kafka-streams/) 11 | - [Spring Cloud Events](chapter-5-spring-cloud-events/) 12 | - [Quarkus Cloud Events](chapter-5-quarkus-cloud-events/) -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .project 3 | .classpath 4 | .settings/ 5 | bin/ 6 | 7 | # IntelliJ 8 | .idea 9 | *.ipr 10 | *.iml 11 | *.iws 12 | 13 | # NetBeans 14 | nb-configuration.xml 15 | 16 | # Visual Studio Code 17 | .vscode 18 | .factorypath 19 | 20 | # OSX 21 | .DS_Store 22 | 23 | # Vim 24 | *.swp 25 | *.swo 26 | 27 | # patch 28 | *.orig 29 | *.rej 30 | 31 | # Maven 32 | target/ 33 | pom.xml.tag 34 | pom.xml.releaseBackup 35 | pom.xml.versionsBackup 36 | release.properties -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-quarkus-cloud-events/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/func.yaml: -------------------------------------------------------------------------------- 1 | name: chapter-5-quarkus-cloud-events 2 | namespace: "" 3 | runtime: quarkus 4 | image: "" 5 | imageDigest: "" 6 | trigger: events 7 | builder: quay.io/boson/faas-quarkus-jvm-builder 8 | builderMap: 9 | default: quay.io/boson/faas-quarkus-jvm-builder 10 | jvm: quay.io/boson/faas-quarkus-jvm-builder 11 | native: quay.io/boson/faas-quarkus-native-builder 12 | env: {} 13 | annotations: {} 14 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-cloud-events . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-cloud-events 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-5-quarkus-cloud-events . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-cloud-events 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-cloud-events . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-cloud-events 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/java/org/acme/domain/Input.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class Input { 4 | private String input; 5 | 6 | public Input() { 7 | 8 | } 9 | 10 | public Input(String input) { 11 | this.input = input; 12 | } 13 | 14 | public String getInput() { 15 | return this.input; 16 | } 17 | 18 | public void setInput(String input) { 19 | this.input = input; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Input{" + 25 | "input='" + this.input + '\'' + 26 | '}'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/java/org/acme/domain/Output.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class Output { 4 | 5 | private String input; 6 | private String operation; 7 | private String output; 8 | private String error; 9 | 10 | public Output() { 11 | } 12 | 13 | public Output(String input, String operation, String output, String error) { 14 | this.input = input; 15 | this.operation = operation; 16 | this.output = output; 17 | this.error = error; 18 | } 19 | 20 | public String getInput() { 21 | return this.input; 22 | } 23 | 24 | public void setInput(String input) { 25 | this.input = input; 26 | } 27 | 28 | public String getOperation() { 29 | return this.operation; 30 | } 31 | 32 | public void setOperation(String operation) { 33 | this.operation = operation; 34 | } 35 | 36 | public String getOutput() { 37 | return this.output; 38 | } 39 | 40 | public void setOutput(String output) { 41 | this.output = output; 42 | } 43 | 44 | public String getError() { 45 | return this.error; 46 | } 47 | 48 | public void setError(String error) { 49 | this.error = error; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Output{" + 55 | "input='" + this.input + '\'' + 56 | ", operation='" + this.operation + '\'' + 57 | ", output='" + this.output + '\'' + 58 | ", error='" + this.error + '\'' + 59 | '}'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/java/org/acme/function/ToUppercaseFunction.java: -------------------------------------------------------------------------------- 1 | package org.acme.function; 2 | 3 | import java.util.Optional; 4 | 5 | import org.acme.domain.Input; 6 | import org.acme.domain.Output; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import io.quarkus.funqy.Context; 11 | import io.quarkus.funqy.Funq; 12 | import io.quarkus.funqy.knative.events.CloudEvent; 13 | 14 | public class ToUppercaseFunction { 15 | private static final Logger LOGGER = LoggerFactory.getLogger(ToUppercaseFunction.class); 16 | 17 | @Funq("uppercase") 18 | public Output function(Input input, @Context CloudEvent cloudEvent) { 19 | LOGGER.info("Input CE Id: {}", cloudEvent.id()); 20 | LOGGER.info("Input CE Spec Version: {}", cloudEvent.specVersion()); 21 | LOGGER.info("Input CE Source: {}", cloudEvent.source()); 22 | LOGGER.info("Input CE Subject: {}", cloudEvent.subject()); 23 | LOGGER.info("Input: {}", input); 24 | 25 | String inputStr = input.getInput(); 26 | String outputStr = Optional.ofNullable(inputStr) 27 | .map(String::toUpperCase) 28 | .orElse("NO DATA"); 29 | 30 | LOGGER.info("Output CE: {}", outputStr); 31 | return new Output(inputStr, cloudEvent.subject(), outputStr, null); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-cloud-events/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.funqy.export=uppercase 2 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-quarkus-kafka-streams/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/create-topics.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | kafka-topics --bootstrap-server localhost:9092 --create --partitions 4 --replication-factor 1 --topic prices 3 | kafka-topics --bootstrap-server localhost:9092 --list -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | 5 | zookeeper: 6 | image: quay.io/strimzi/kafka:0.29.0-kafka-3.1.1 7 | command: [ 8 | "sh", "-c", 9 | "bin/zookeeper-server-start.sh config/zookeeper.properties" 10 | ] 11 | ports: 12 | - "2181:2181" 13 | environment: 14 | LOG_DIR: /tmp/logs 15 | 16 | kafka: 17 | image: quay.io/strimzi/kafka:0.29.0-kafka-3.1.1 18 | command: [ 19 | "sh", "-c", 20 | "bin/kafka-server-start.sh config/server.properties --override listeners=$${KAFKA_LISTENERS} --override advertised.listeners=$${KAFKA_ADVERTISED_LISTENERS} --override zookeeper.connect=$${KAFKA_ZOOKEEPER_CONNECT}" 21 | ] 22 | depends_on: 23 | - zookeeper 24 | ports: 25 | - "9092:9092" 26 | environment: 27 | LOG_DIR: "/tmp/logs" 28 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 29 | KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 30 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-kafka-streams . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-kafka-streams 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-5-quarkus-kafka-streams . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-kafka-streams 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-kafka-streams . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-kafka-streams 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/java/org/acme/rest/PriceResource.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | import org.eclipse.microprofile.reactive.messaging.Channel; 9 | import org.reactivestreams.Publisher; 10 | 11 | /** 12 | * A simple resource retrieving the "in-memory" "my-data-stream" and sending the items to a server sent event. 13 | */ 14 | @Path("/prices") 15 | public class PriceResource { 16 | 17 | private final Publisher prices; 18 | 19 | public PriceResource(@Channel("my-data-stream") Publisher prices) { 20 | this.prices = prices; 21 | } 22 | 23 | @GET 24 | @Path("/stream") 25 | @Produces(MediaType.SERVER_SENT_EVENTS) // denotes that server side events (SSE) will be produced 26 | public Publisher stream() { 27 | return this.prices; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/java/org/acme/service/PriceConverter.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | 5 | import org.eclipse.microprofile.reactive.messaging.Incoming; 6 | import org.eclipse.microprofile.reactive.messaging.Outgoing; 7 | 8 | import io.smallrye.reactive.messaging.annotations.Broadcast; 9 | 10 | /** 11 | * A bean consuming data from the "prices" Kafka topic and applying some conversion. 12 | * The result is pushed to the "my-data-stream" stream which is an in-memory stream. 13 | */ 14 | @ApplicationScoped 15 | public class PriceConverter { 16 | static final double CONVERSION_RATE = 0.88; 17 | 18 | // Consume from the `prices` channel and produce to the `my-data-stream` channel 19 | @Incoming("prices") 20 | @Outgoing("my-data-stream") 21 | 22 | // Send to all subscribers and Acknowledge the messages before calling this method 23 | @Broadcast 24 | public double process(int priceInUsd) { 25 | return priceInUsd * CONVERSION_RATE; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/java/org/acme/service/PriceGenerator.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.time.Duration; 4 | import java.util.Random; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | 8 | import org.eclipse.microprofile.reactive.messaging.Outgoing; 9 | 10 | import io.smallrye.mutiny.Multi; 11 | 12 | /** 13 | * A bean producing random prices every 5 seconds. 14 | * The prices are written to a Kafka topic (prices). The Kafka configuration is specified in the application configuration. 15 | */ 16 | @ApplicationScoped 17 | public class PriceGenerator { 18 | 19 | private final Random random = new Random(); 20 | 21 | @Outgoing("generated-price") 22 | public Multi generate() { 23 | return Multi.createFrom().ticks().every(Duration.ofSeconds(5)) 24 | .onOverflow().drop() 25 | .map(tick -> this.random.nextInt(100)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/resources/META-INF/resources/prices.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Prices 6 | 7 | 9 | 11 | 12 | 13 |
14 | 15 |

Last price

16 |
17 |

The last price is N/A €.

18 |
19 |
20 | 21 | 22 | 28 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Configure the Kafka sink (we write to it) 2 | mp.messaging.outgoing.generated-price.connector=smallrye-kafka 3 | mp.messaging.outgoing.generated-price.topic=prices 4 | mp.messaging.outgoing.generated-price.value.serializer=org.apache.kafka.common.serialization.IntegerSerializer 5 | 6 | # Configure the Kafka source (we read from it) 7 | mp.messaging.incoming.prices.connector=smallrye-kafka 8 | mp.messaging.incoming.prices.health-readiness-enabled=false 9 | mp.messaging.incoming.prices.topic=prices 10 | mp.messaging.incoming.prices.value.deserializer=org.apache.kafka.common.serialization.IntegerDeserializer 11 | 12 | # vectorized/redpanda images are gone 13 | quarkus.kafka.devservices.image-name=redpandadata/redpanda:v22.3.4 -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/test/java/org/acme/rest/PriceResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.net.URI; 6 | import java.time.Duration; 7 | import java.util.List; 8 | 9 | import javax.ws.rs.client.ClientBuilder; 10 | import javax.ws.rs.core.MediaType; 11 | 12 | import org.jboss.resteasy.reactive.client.impl.MultiInvoker; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import io.quarkus.test.common.http.TestHTTPEndpoint; 16 | import io.quarkus.test.common.http.TestHTTPResource; 17 | import io.quarkus.test.junit.QuarkusTest; 18 | import io.smallrye.mutiny.helpers.test.AssertSubscriber; 19 | 20 | @QuarkusTest 21 | class PriceResourceTest { 22 | @TestHTTPEndpoint(PriceResource.class) 23 | @TestHTTPResource("/stream") 24 | URI uri; 25 | 26 | @Test 27 | public void sseEventStream() { 28 | List received = ClientBuilder.newClient() 29 | .target(this.uri) 30 | .request(MediaType.SERVER_SENT_EVENTS) 31 | .rx(MultiInvoker.class) 32 | .get(Double.class) 33 | .select().first(3) 34 | .subscribe().withSubscriber(AssertSubscriber.create(3)) 35 | .assertSubscribed() 36 | .awaitItems(3, Duration.ofSeconds(20)) 37 | .assertCompleted() 38 | .getItems(); 39 | 40 | assertThat(received) 41 | .hasSize(3) 42 | .allMatch(value -> (value >= 0) && (value < 100)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/test/java/org/acme/service/PriceConverterTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.DisplayName; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.ValueSource; 8 | 9 | class PriceConverterTests { 10 | PriceConverter priceConverter = new PriceConverter(); 11 | 12 | @ParameterizedTest(name = ParameterizedTest.DISPLAY_NAME_PLACEHOLDER + "[" + ParameterizedTest.INDEX_PLACEHOLDER + "] (" + ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER + ")") 13 | @ValueSource(ints = { 1, 2 }) 14 | @DisplayName("process") 15 | public void process(int price) { 16 | assertThat(this.priceConverter.process(price)) 17 | .isEqualTo(price * PriceConverter.CONVERSION_RATE); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-kafka-streams/src/test/java/org/acme/service/PriceGeneratorTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import io.smallrye.mutiny.helpers.test.AssertSubscriber; 11 | 12 | class PriceGeneratorTests { 13 | PriceGenerator priceGenerator = new PriceGenerator(); 14 | 15 | @Test 16 | public void generatesProperly() { 17 | List prices = this.priceGenerator.generate() 18 | .select().first(2) 19 | .subscribe().withSubscriber(AssertSubscriber.create()) 20 | .assertSubscribed() 21 | .awaitNextItem(Duration.ofSeconds(10)) // Needs to be a few seconds more than the timing of actual events 22 | .awaitNextItem(Duration.ofSeconds(15)) 23 | .awaitCompletion(Duration.ofSeconds(20)) 24 | .assertCompleted() 25 | .getItems(); 26 | 27 | assertThat(prices) 28 | .hasSize(2) 29 | .allMatch(value -> (value >= 0) && (value < 100)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-quarkus-vertx-eventbus/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-vertx-eventbus . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-vertx-eventbus 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-5-quarkus-vertx-eventbus . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-vertx-eventbus 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-5-quarkus-vertx-eventbus . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-5-quarkus-vertx-eventbus 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/java/org/acme/GreetingResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.PathParam; 6 | import javax.ws.rs.Produces; 7 | import javax.ws.rs.core.MediaType; 8 | 9 | import io.smallrye.mutiny.Uni; 10 | import io.vertx.mutiny.core.eventbus.EventBus; 11 | import io.vertx.mutiny.core.eventbus.Message; 12 | 13 | @Path("/async") 14 | public class GreetingResource { 15 | 16 | private final EventBus bus; 17 | 18 | public GreetingResource(EventBus bus) { 19 | this.bus = bus; 20 | } 21 | 22 | @GET 23 | @Produces(MediaType.TEXT_PLAIN) 24 | @Path("{name}") 25 | public Uni greeting(@PathParam("name") String name) { 26 | return this.bus.request("greeting", name) 27 | .map(Message::body); 28 | } 29 | 30 | @GET 31 | @Produces(MediaType.TEXT_PLAIN) 32 | @Path("block/{message}") 33 | public Uni blockingConsumer(String message) { 34 | return this.bus.request("blocking-consumer", message) 35 | .map(Message::body); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/java/org/acme/GreetingService.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.smallrye.common.annotation.Blocking; 4 | import io.quarkus.vertx.ConsumeEvent; 5 | 6 | import javax.enterprise.context.ApplicationScoped; 7 | 8 | @ApplicationScoped 9 | public class GreetingService { 10 | 11 | @ConsumeEvent("greeting") 12 | public String consume(String name) { 13 | return name.toUpperCase(); 14 | } 15 | 16 | @ConsumeEvent("blocking-consumer") 17 | @Blocking 18 | public String consumeBlocking(String message) { 19 | return "Processing Blocking I/O: " + message; 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/resources/application.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-quarkus-vertx-eventbus/src/main/resources/application.properties -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = false 8 | insert_final_newline = false 9 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-spring-cloud-events/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/func.yaml: -------------------------------------------------------------------------------- 1 | name: chapter-5-spring-cloud-events 2 | namespace: "" 3 | runtime: springboot 4 | image: "" 5 | imageDigest: "" 6 | trigger: events 7 | builder: quay.io/boson/faas-springboot-builder 8 | builderMap: 9 | default: quay.io/boson/faas-springboot-builder 10 | env: {} 11 | annotations: {} -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/src/main/java/org/acme/SpringCloudEventsApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.UUID; 4 | 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cloud.function.cloudevent.CloudEventHeaderEnricher; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | @SpringBootApplication 11 | public class SpringCloudEventsApplication { 12 | public static void main(String[] args) { 13 | SpringApplication.run(SpringCloudEventsApplication.class, args); 14 | } 15 | 16 | @Bean 17 | public CloudEventHeaderEnricher attributesProvider() { 18 | return attributes -> attributes 19 | .setSpecVersion("1.0") 20 | .setId(UUID.randomUUID().toString()) 21 | .setSource("http://example.com/uppercase") 22 | .setType("com.redhat.faas.springboot.events"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/src/main/java/org/acme/domain/Input.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class Input { 4 | private String input; 5 | 6 | public Input() { 7 | 8 | } 9 | 10 | public Input(String input) { 11 | this.input = input; 12 | } 13 | 14 | public String getInput() { 15 | return this.input; 16 | } 17 | 18 | public void setInput(String input) { 19 | this.input = input; 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "Input{" + 25 | "input='" + this.input + '\'' + 26 | '}'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-cloud-events/src/main/java/org/acme/domain/Output.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class Output { 4 | 5 | private String input; 6 | private String operation; 7 | private String output; 8 | private String error; 9 | 10 | public Output() { 11 | } 12 | 13 | public Output(String input, String operation, String output, String error) { 14 | this.input = input; 15 | this.operation = operation; 16 | this.output = output; 17 | this.error = error; 18 | } 19 | 20 | public String getInput() { 21 | return this.input; 22 | } 23 | 24 | public void setInput(String input) { 25 | this.input = input; 26 | } 27 | 28 | public String getOperation() { 29 | return this.operation; 30 | } 31 | 32 | public void setOperation(String operation) { 33 | this.operation = operation; 34 | } 35 | 36 | public String getOutput() { 37 | return this.output; 38 | } 39 | 40 | public void setOutput(String output) { 41 | this.output = output; 42 | } 43 | 44 | public String getError() { 45 | return this.error; 46 | } 47 | 48 | public void setError(String error) { 49 | this.error = error; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Output{" + 55 | "input='" + this.input + '\'' + 56 | ", operation='" + this.operation + '\'' + 57 | ", output='" + this.output + '\'' + 58 | ", error='" + this.error + '\'' + 59 | '}'; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-spring-eventbus/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/main/java/org/acme/chapter5springeventbus/Chapter5SpringEventbusApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter5SpringEventbusApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Chapter5SpringEventbusApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/main/java/org/acme/chapter5springeventbus/config/ChannelConfig.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.integration.dsl.MessageChannels; 6 | import org.springframework.messaging.MessageChannel; 7 | 8 | @Configuration 9 | public class ChannelConfig { 10 | @Bean 11 | public MessageChannel greetingChannel() { 12 | return MessageChannels.flux("greeting").get(); 13 | } 14 | 15 | @Bean 16 | public MessageChannel blockingGreetingChannel() { 17 | return MessageChannels.direct("blocking-greeting").get(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/main/java/org/acme/chapter5springeventbus/rest/GreetingController.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus.rest; 2 | 3 | import org.acme.chapter5springeventbus.service.GreetingGateway; 4 | 5 | import org.springframework.http.MediaType; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import reactor.core.publisher.Mono; 12 | 13 | @RestController 14 | @RequestMapping("/async") 15 | public class GreetingController { 16 | private final GreetingGateway greetingGateway; 17 | 18 | public GreetingController(GreetingGateway greetingGateway) { 19 | this.greetingGateway = greetingGateway; 20 | } 21 | 22 | @GetMapping(path = "/{name}", produces = MediaType.TEXT_PLAIN_VALUE) 23 | public Mono greeting(@PathVariable String name) { 24 | return this.greetingGateway.greeting(Mono.justOrEmpty(name)); 25 | } 26 | 27 | @GetMapping(path = "/block/{message}", produces = MediaType.TEXT_PLAIN_VALUE) 28 | public Mono blockingConsumer(@PathVariable String message) { 29 | return Mono.justOrEmpty(this.greetingGateway.blockingGreeting(message)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/main/java/org/acme/chapter5springeventbus/service/GreetingGateway.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus.service; 2 | 3 | import org.springframework.integration.annotation.Gateway; 4 | import org.springframework.integration.annotation.MessagingGateway; 5 | 6 | import reactor.core.publisher.Mono; 7 | 8 | @MessagingGateway 9 | public interface GreetingGateway { 10 | @Gateway(requestChannel = "greeting") 11 | Mono greeting(Mono input); 12 | 13 | @Gateway(requestChannel = "blocking-greeting") 14 | String blockingGreeting(String input); 15 | } 16 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/main/java/org/acme/chapter5springeventbus/service/GreetingService.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus.service; 2 | 3 | import org.springframework.integration.annotation.MessageEndpoint; 4 | import org.springframework.integration.annotation.ServiceActivator; 5 | 6 | import reactor.core.publisher.Mono; 7 | 8 | @MessageEndpoint 9 | public class GreetingService { 10 | @ServiceActivator(inputChannel = "greeting", async = "true") 11 | public Mono consume(Mono name) { 12 | return name.map(String::toUpperCase); 13 | } 14 | 15 | @ServiceActivator(inputChannel = "blocking-greeting") 16 | public String consumeBlocking(String message) { 17 | return "Processing Blocking I/O: " + message; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/test/java/org/acme/chapter5springeventbus/Chapter5SpringEventbusApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class Chapter5SpringEventbusApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-eventbus/src/test/java/org/acme/chapter5springeventbus/service/GreetingServiceTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.chapter5springeventbus.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import reactor.core.publisher.Mono; 8 | import reactor.test.StepVerifier; 9 | 10 | class GreetingServiceTests { 11 | @Test 12 | public void consume() { 13 | StepVerifier.create(new GreetingService().consume(Mono.just("hi"))) 14 | .expectNext("HI") 15 | .verifyComplete(); 16 | } 17 | 18 | @Test 19 | public void consumeBlocking() { 20 | assertThat(new GreetingService().consumeBlocking("hi")) 21 | .isEqualTo("Processing Blocking I/O: hi"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-5/chapter-5-spring-kafka-streams/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/create-topics.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | kafka-topics --bootstrap-server localhost:9092 --create --partitions 4 --replication-factor 1 --topic prices 3 | kafka-topics --bootstrap-server localhost:9092 --list -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | services: 4 | 5 | zookeeper: 6 | image: quay.io/strimzi/kafka:0.29.0-kafka-3.1.1 7 | command: [ 8 | "sh", "-c", 9 | "bin/zookeeper-server-start.sh config/zookeeper.properties" 10 | ] 11 | ports: 12 | - "2181:2181" 13 | environment: 14 | LOG_DIR: /tmp/logs 15 | 16 | kafka: 17 | image: quay.io/strimzi/kafka:0.29.0-kafka-3.1.1 18 | command: [ 19 | "sh", "-c", 20 | "bin/kafka-server-start.sh config/server.properties --override listeners=$${KAFKA_LISTENERS} --override advertised.listeners=$${KAFKA_ADVERTISED_LISTENERS} --override zookeeper.connect=$${KAFKA_ZOOKEEPER_CONNECT}" 21 | ] 22 | depends_on: 23 | - zookeeper 24 | ports: 25 | - "9092:9092" 26 | environment: 27 | LOG_DIR: "/tmp/logs" 28 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 29 | KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 30 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/java/org/acme/Chapter5SpringKafkaStreamsApplication.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Chapter5SpringKafkaStreamsApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Chapter5SpringKafkaStreamsApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/java/org/acme/config/InMemoryChannelConfig.java: -------------------------------------------------------------------------------- 1 | package org.acme.config; 2 | 3 | import org.acme.service.InMemoryChannel; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class InMemoryChannelConfig { 10 | @Bean 11 | public InMemoryChannel inMemoryChannel() { 12 | return new InMemoryChannel<>(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/java/org/acme/rest/PriceController.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import org.acme.service.InMemoryChannel; 4 | 5 | import org.springframework.http.MediaType; 6 | import org.springframework.http.codec.ServerSentEvent; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import reactor.core.publisher.Flux; 12 | 13 | @RestController 14 | @RequestMapping("/prices") 15 | public class PriceController { 16 | private final InMemoryChannel inMemoryChannel; 17 | 18 | public PriceController(InMemoryChannel inMemoryChannel) { 19 | this.inMemoryChannel = inMemoryChannel; 20 | } 21 | 22 | @GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 23 | public Flux> stream() { 24 | return Flux.from(this.inMemoryChannel.getPublisher()) 25 | .map(price -> ServerSentEvent.builder(price).build()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/java/org/acme/service/PriceConverter.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.util.function.Consumer; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import reactor.core.publisher.Flux; 8 | 9 | @Component("priceconverter") 10 | public class PriceConverter implements Consumer> { 11 | static final double CONVERSION_RATE = 0.88; 12 | 13 | private final InMemoryChannel inMemoryChannel; 14 | 15 | public PriceConverter(InMemoryChannel inMemoryChannel) { 16 | this.inMemoryChannel = inMemoryChannel; 17 | } 18 | 19 | @Override 20 | public void accept(Flux priceInUsd) { 21 | priceInUsd 22 | .map(price -> price * CONVERSION_RATE) 23 | .subscribe(this.inMemoryChannel::emitValue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/java/org/acme/service/PriceGenerator.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.time.Duration; 4 | import java.util.Random; 5 | import java.util.function.Supplier; 6 | 7 | import org.springframework.stereotype.Component; 8 | 9 | import reactor.core.publisher.Flux; 10 | 11 | @Component("generateprice") 12 | public class PriceGenerator implements Supplier> { 13 | private final Random random = new Random(); 14 | 15 | @Override 16 | public Flux get() { 17 | return Flux.interval(Duration.ofSeconds(5)) 18 | .onBackpressureDrop() 19 | .map(tick -> this.random.nextInt(100)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Spring Cloud Stream config 2 | spring.cloud.stream.function.definition=generateprice;priceconverter 3 | 4 | # Configure the Kafka sink (we write to it) 5 | spring.cloud.stream.bindings.generateprice-out-0.destination=prices 6 | spring.cloud.stream.bindings.generateprice-out-0.producer.use-native-encoding=true 7 | spring.cloud.stream.kafka.bindings.generateprice-out-0.producer.configuration.value.serializer=org.apache.kafka.common.serialization.IntegerSerializer 8 | 9 | # Configure the Kafka source (we read from it) 10 | spring.cloud.stream.bindings.priceconverter-in-0.destination=prices 11 | spring.cloud.stream.bindings.priceconverter-in-0.consumer.use-native-decoding=true 12 | spring.cloud.stream.bindings.priceconverter-in-0.group=priceConsumer 13 | spring.cloud.stream.kafka.bindings.priceconverter-in-0.consumer.configuration.value.deserializer=org.apache.kafka.common.serialization.IntegerDeserializer 14 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/main/resources/static/prices.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Prices 6 | 7 | 9 | 11 | 12 | 13 |
14 | 15 |

Last price

16 |
17 |

The last price is N/A €.

18 |
19 |
20 | 21 | 22 | 28 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/test/java/org/acme/Chapter5SpringKafkaStreamsApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | 8 | import org.junit.jupiter.api.Test; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.test.web.reactive.server.WebTestClient; 16 | 17 | @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) 18 | @AutoConfigureWebTestClient(timeout = "PT10M") 19 | class Chapter5SpringKafkaStreamsApplicationTests extends DockerComposeBase { 20 | @Autowired 21 | WebTestClient webTestClient; 22 | 23 | @Test 24 | void sseWorks() { 25 | List emittedPrices = this.webTestClient 26 | .get() 27 | .uri("/prices/stream") 28 | .exchange() 29 | .expectStatus().isOk() 30 | .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM) 31 | .returnResult(Double.class) 32 | .getResponseBody() 33 | .take(3) 34 | .collectList() 35 | .block(Duration.ofMinutes(10)); 36 | 37 | assertThat(emittedPrices) 38 | .isNotNull() 39 | .hasSize(3) 40 | .allMatch(value -> (value >= 0) && (value < 100)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/test/java/org/acme/rest/PriceControllerTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.time.Duration; 4 | 5 | import org.acme.DockerComposeBase; 6 | import org.acme.service.InMemoryChannel; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.Mockito; 9 | 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.http.MediaType; 15 | import org.springframework.test.web.reactive.server.WebTestClient; 16 | 17 | import reactor.core.publisher.Flux; 18 | 19 | @SpringBootTest 20 | @AutoConfigureWebTestClient 21 | class PriceControllerTests extends DockerComposeBase { 22 | @Autowired 23 | WebTestClient webTestClient; 24 | 25 | @MockBean 26 | InMemoryChannel inMemoryChannel; 27 | 28 | @Test 29 | public void stream() { 30 | Mockito.when(this.inMemoryChannel.getPublisher()) 31 | .thenReturn(Flux.just(1.1, 2.2).delayElements(Duration.ofSeconds(1))); 32 | 33 | this.webTestClient.get() 34 | .uri("/prices/stream") 35 | .exchange() 36 | .expectStatus().isOk() 37 | .expectHeader().contentTypeCompatibleWith(MediaType.TEXT_EVENT_STREAM) 38 | .expectBody(String.class).isEqualTo("data:1.1\n\ndata:2.2\n\n"); 39 | 40 | Mockito.verify(this.inMemoryChannel).getPublisher(); 41 | Mockito.verifyNoMoreInteractions(this.inMemoryChannel); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/test/java/org/acme/service/PriceConverterTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.time.Duration; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import reactor.core.publisher.Flux; 8 | import reactor.test.StepVerifier; 9 | import reactor.test.publisher.PublisherProbe; 10 | 11 | class PriceConverterTests { 12 | @Test 13 | public void emitsToSinkProperly() { 14 | InMemoryChannel inMemoryChannel = new InMemoryChannel<>(); 15 | 16 | Flux integerFlux = Flux.just(1, 2) 17 | .delayElements(Duration.ofSeconds(1)); 18 | 19 | PublisherProbe probe = PublisherProbe.of(integerFlux); 20 | new PriceConverter(inMemoryChannel).accept(probe.flux()); 21 | 22 | StepVerifier.create(Flux.from(inMemoryChannel.getPublisher()).take(2)) 23 | .expectNext(1 * PriceConverter.CONVERSION_RATE) 24 | .expectNext(2 * PriceConverter.CONVERSION_RATE) 25 | .expectComplete() 26 | .verify(Duration.ofSeconds(5)); 27 | 28 | probe.assertWasSubscribed(); 29 | probe.assertWasNotCancelled(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/test/java/org/acme/service/PriceGeneratorTests.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import java.time.Duration; 4 | import java.util.function.Predicate; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import reactor.test.StepVerifier; 9 | 10 | class PriceGeneratorTests { 11 | PriceGenerator generator = new PriceGenerator(); 12 | 13 | private static final Predicate VALUE_TEST = 14 | value -> (value >= 0) && (value < 100); 15 | 16 | @Test 17 | public void generatesProperly() { 18 | StepVerifier 19 | .withVirtualTime(() -> this.generator.get().take(2)) 20 | .expectSubscription() 21 | .expectNoEvent(Duration.ofSeconds(5)) 22 | .expectNextMatches(VALUE_TEST) 23 | .thenAwait(Duration.ofSeconds(5)) 24 | .expectNextMatches(VALUE_TEST) 25 | .expectComplete() 26 | .verify(Duration.ofSeconds(15)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chapter-5/chapter-5-spring-kafka-streams/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /chapter-6/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 6 - Building Applications for the Cloud 2 | 3 | This directory contains the examples used within the chapter 6 `Building Applications for the Cloud`. 4 | 5 | They have been played successfully using a kubernetes cluster `1.21`. The cluster has been created using the 6 | tool [Kind](https://kind.sigs.k8s.io/) which uses a Docker container as kubernetes `node`. 7 | 8 | To install it on your laptop, feel free to use our bash script available [here](https://github.com/snowdrop/k8s-infra/blob/master/kind/kind-reg-ingress.sh). 9 | It will install and/or delete a cluster, runs a docker local registry, install the Ingress controller and configure some `NodePorts` needed for the remote debugging example ;-) 10 | 11 | ## Examples 12 | 13 | - [Quarkus RESTful application](chapter-6-quarkus-rest/) 14 | - [Quarkus Hibernate & PostgreSQL application using Service Binding](chapter-6-quarkus-rest-database/) 15 | - [Quarkus REST application & remote developing](chapter-6-quarkus-rest-debug/) 16 | - [Quarkus and configuration externalized](chapter-6-quarkus-rest-config/) 17 | - [Using a Cloud config server](chapter-6-quarkus-rest-cloud-config/) 18 | - [Monitoring, metrics and distributed traces](chapter-6-quarkus-rest-monitoring/) 19 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-cloud-config/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-cloud-config . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-cloud-config 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest-cloud-config . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-cloud-config 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-cloud-config . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-cloud-config 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/java/org/acme/GreeterResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("/hello") 9 | public class GreeterResource { 10 | private final GreetingProperties greetingProperties; 11 | 12 | public GreeterResource(GreetingProperties greetingProperties) { 13 | this.greetingProperties = greetingProperties; 14 | } 15 | 16 | @GET 17 | @Produces(MediaType.TEXT_PLAIN) 18 | public String hello() { 19 | return this.greetingProperties.message() + " " + this.greetingProperties.name().orElse("world") + this.greetingProperties.suffix(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/java/org/acme/GreetingProperties.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.Optional; 4 | 5 | import io.smallrye.config.ConfigMapping; 6 | import io.smallrye.config.WithDefault; 7 | 8 | @ConfigMapping(prefix = "greeting") 9 | public interface GreetingProperties { 10 | String message(); 11 | 12 | @WithDefault("!") 13 | String suffix(); 14 | 15 | Optional name(); 16 | } 17 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/k8s/helm.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Helm chart to install the Spring Cloud Config server 3 | # Commands to be executed 4 | # kubectl create ns config-storage 5 | # helm repo add kiwigrid https://kiwigrid.github.io 6 | # helm install \ 7 | # spring-cloud-config-server kiwigrid/spring-cloud-config-server \ 8 | # -n config-storage \ 9 | # -f src/main/configs/helm.yml 10 | # 11 | # helm delete spring-cloud-config-server -n config-storage 12 | # 13 | config: 14 | gitUri: https://github.com/quarkus-for-spring-developers/sccs-config-repo 15 | gitSearchpath: {application} 16 | extraEnv: 17 | - name: SPRING_CLOUD_CONFIG_SERVER_GIT_DEFAULT-LABEL 18 | value: main 19 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.kubernetes.ingress.expose=true 2 | quarkus.kubernetes.ingress.host=chapter-6-quarkus-rest-cloud-config.127.0.0.1.nip.io 3 | 4 | # use the same name as the application name that was configured when standing up the Config Server 5 | quarkus.application.name=greeting-application 6 | # enable retrieval of configuration from the Config Server - this is off by default 7 | quarkus.spring-cloud-config.enabled=true 8 | %test.greeting.message=Hello cloud config 9 | %test.greeting.name=quarkus 10 | 11 | # 12 | # Configure the URL of the Spring Cloud Config Server deployed on k8s 13 | # where it will listen to the HTTP requests 14 | # 15 | quarkus.spring-cloud-config.url=http://spring-cloud-config-server.config-storage:80 16 | 17 | # Please uncomment me and specify the correct values 18 | # quarkus.container-image.registry=quay.io 19 | # quarkus.container-image.group= 20 | # quarkus.container-image.name=chapter-6-quarkus-rest-cloud-config 21 | # quarkus.container-image.tag=1.0 22 | # quarkus.container-image.build=true 23 | # quarkus.container-image.push=true 24 | # quarkus.container-image.username= 25 | # quarkus.container-image.password= 26 | 27 | 28 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/test/java/org/acme/GreeterResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | @QuarkusTest 10 | public class GreeterResourceTest { 11 | 12 | @Test 13 | public void testHelloEndpoint() { 14 | given() 15 | .when().get("/hello") 16 | .then() 17 | .statusCode(200) 18 | .body(is("Hello cloud config quarkus!")); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-cloud-config/src/test/java/org/acme/NativeGreeterResourceIT.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.NativeImageTest; 4 | 5 | @NativeImageTest 6 | public class NativeGreeterResourceIT extends GreeterResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-config/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/configs/greeting-env: -------------------------------------------------------------------------------- 1 | greeting.message=Hello configmap 2 | greeting.name=quarkus 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-config . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-config 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest-config . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-config 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-config . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-config 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/java/org/acme/GreeterResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("/hello") 9 | public class GreeterResource { 10 | private final GreetingProperties greetingProperties; 11 | 12 | public GreeterResource(GreetingProperties greetingProperties) { 13 | this.greetingProperties = greetingProperties; 14 | } 15 | 16 | @GET 17 | @Produces(MediaType.TEXT_PLAIN) 18 | public String hello() { 19 | return this.greetingProperties.message() + " " + this.greetingProperties.name().orElse("world") + this.greetingProperties.suffix(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/java/org/acme/GreetingProperties.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import java.util.Optional; 4 | 5 | import io.smallrye.config.ConfigMapping; 6 | import io.smallrye.config.WithDefault; 7 | 8 | @ConfigMapping(prefix = "greeting") 9 | public interface GreetingProperties { 10 | String message(); 11 | 12 | @WithDefault("!") 13 | String suffix(); 14 | 15 | Optional name(); 16 | } 17 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.kubernetes.ingress.expose=true 2 | quarkus.kubernetes.ingress.host=chapter-6-quarkus-rest-config.127.0.0.1.nip.io 3 | 4 | # 5 | # Properties allowing to set the greeting message 6 | # 7 | greeting.message=Hello 8 | greeting.name=quarkus 9 | 10 | # 11 | # Properties to be used to generate the Kubernetes environment variables 12 | # to pass the greeting message and greeting name 13 | # 14 | # quarkus.kubernetes.env.vars."greeting.message"=Hello from Kubernetes Env var to 15 | # quarkus.kubernetes.env.vars."greeting.name"=Quarkus developers 16 | 17 | # 18 | # Property to be used to import the env variables from a Kubernetes ConfigMap created using the command 19 | # kubectl create -n quarkus-demo-config configmap greeting-map --from-env-file=src/main/configs/greeting-env 20 | # 21 | # quarkus.kubernetes.env.configmaps=greeting-map 22 | 23 | # Please uncomment me and specify the correct values 24 | # quarkus.container-image.registry=quay.io 25 | # quarkus.container-image.group= 26 | # quarkus.container-image.name=chapter-6-quarkus-rest-config 27 | # quarkus.container-image.tag=1.0 28 | # quarkus.container-image.build=true 29 | # quarkus.container-image.push=true 30 | # quarkus.container-image.username= 31 | # quarkus.container-image.password= 32 | 33 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/test/java/org/acme/GreeterResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | @QuarkusTest 10 | public class GreeterResourceTest { 11 | 12 | @Test 13 | public void testHelloEndpoint() { 14 | given() 15 | .when().get("/hello") 16 | .then() 17 | .statusCode(200) 18 | .body(is("Hello quarkus!")); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-config/src/test/java/org/acme/NativeGreeterResourceIT.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.NativeImageTest; 4 | 5 | @NativeImageTest 6 | public class NativeGreeterResourceIT extends GreeterResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .project 3 | .classpath 4 | .settings/ 5 | bin/ 6 | 7 | # IntelliJ 8 | .idea 9 | *.ipr 10 | *.iml 11 | *.iws 12 | 13 | # NetBeans 14 | nb-configuration.xml 15 | 16 | # Visual Studio Code 17 | .vscode 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | # Vim 23 | *.swp 24 | *.swo 25 | 26 | # patch 27 | *.orig 28 | *.rej 29 | 30 | # Maven 31 | target/ 32 | pom.xml.tag 33 | pom.xml.releaseBackup 34 | pom.xml.versionsBackup 35 | release.properties -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-database/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/k8s/postgresql.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: postgres-configuration 6 | labels: 7 | app: postgresql 8 | data: 9 | POSTGRES_DB: fruits 10 | POSTGRES_USER: fruits 11 | POSTGRES_PASSWORD: fruits 12 | --- 13 | apiVersion: apps/v1 14 | kind: Deployment 15 | metadata: 16 | labels: 17 | name: postgresql 18 | name: postgresql 19 | spec: 20 | replicas: 1 21 | selector: 22 | matchLabels: 23 | name: postgresql 24 | template: 25 | metadata: 26 | labels: 27 | name: postgresql 28 | spec: 29 | containers: 30 | - image: quay.io/edeandrea/postgres-13-fruits:latest 31 | name: postgresql 32 | ports: 33 | - containerPort: 5432 34 | envFrom: 35 | - configMapRef: 36 | name: postgres-configuration 37 | volumeMounts: 38 | - mountPath: /var/lib/postgresql/data 39 | name: postgresql-data 40 | volumes: 41 | - name: postgresql-data 42 | persistentVolumeClaim: 43 | claimName: postgresql 44 | --- 45 | apiVersion: v1 46 | kind: Service 47 | metadata: 48 | labels: 49 | name: postgresql 50 | name: postgresql 51 | spec: 52 | ports: 53 | - port: 5432 54 | protocol: TCP 55 | targetPort: 5432 56 | selector: 57 | name: postgresql 58 | type: ClusterIP 59 | --- 60 | apiVersion: v1 61 | kind: PersistentVolumeClaim 62 | metadata: 63 | labels: 64 | name: postgresql 65 | name: postgresql 66 | spec: 67 | accessModes: 68 | - ReadWriteOnce 69 | resources: 70 | requests: 71 | storage: 5Gi 72 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/k8s/service-binding-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: fruit-db 5 | data: 6 | host: | 7 | cG9zdGdyZXNxbA== 8 | port: | 9 | NTQzMg== 10 | database: | 11 | ZnJ1aXRz 12 | username: | 13 | ZnJ1aXRz 14 | password: | 15 | ZnJ1aXRz 16 | type: | 17 | cG9zdGdyZXNxbA== -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-database . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-database 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest-database . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-database 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-database . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-database 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/java/org/acme/domain/Fruit.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.Table; 9 | import javax.validation.constraints.NotBlank; 10 | 11 | @Entity 12 | @Table(name = "fruits") 13 | public class Fruit { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | @Column(nullable = false, unique = true) 19 | @NotBlank(message = "Name is mandatory") 20 | private String name; 21 | private String description; 22 | 23 | public Fruit() { 24 | } 25 | 26 | public Fruit(Long id, String name, String description) { 27 | this.id = id; 28 | this.name = name; 29 | this.description = description; 30 | } 31 | 32 | public Fruit(String type) { 33 | this.name = type; 34 | } 35 | 36 | public Long getId() { 37 | return this.id; 38 | } 39 | 40 | public void setId(Long id) { 41 | this.id = id; 42 | } 43 | 44 | public String getName() { 45 | return this.name; 46 | } 47 | 48 | public void setName(String name) { 49 | this.name = name; 50 | } 51 | 52 | public String getDescription() { 53 | return this.description; 54 | } 55 | 56 | public void setDescription(String description) { 57 | this.description = description; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/java/org/acme/repository/FruitRepository.java: -------------------------------------------------------------------------------- 1 | package org.acme.repository; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | 5 | import org.acme.domain.Fruit; 6 | 7 | import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase; 8 | 9 | @ApplicationScoped 10 | public class FruitRepository implements PanacheRepositoryBase { 11 | } 12 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-database/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.hibernate-orm.database.generation=validate 2 | quarkus.datasource.devservices.image-name=quay.io/edeandrea/postgres-13-fruits:latest 3 | 4 | # Please uncomment me and specify the correct values 5 | # quarkus.container-image.registry=quay.io 6 | # quarkus.container-image.group= 7 | # quarkus.container-image.name=chapter-6-quarkus-rest-database 8 | # quarkus.container-image.tag=1.0 9 | # quarkus.container-image.build=true 10 | # quarkus.container-image.push=true 11 | # quarkus.container-image.username= 12 | # quarkus.container-image.password= 13 | 14 | quarkus.kubernetes.ingress.expose=true 15 | quarkus.kubernetes.ingress.host=chapter-6-quarkus-rest-database.127.0.0.1.nip.io 16 | 17 | quarkus.kubernetes-service-binding.root=k8s-sb 18 | quarkus.kubernetes-service-binding.enabled=true 19 | 20 | # Note: As the home directory of the Quarkus application as defined within the “Dockerfile” is “/work”, 21 | # then we will use it as “root” path to mount the secret under the “SERVICE_BINDING_ROOT” filesystem 22 | # using as sub-path the name of the secret which is here “fruit-db. 23 | quarkus.kubernetes.mounts.service-binding-volume.path=/work/k8s-sb/fruits-db 24 | quarkus.kubernetes.secret-volumes.service-binding-volume.secret-name=fruit-db 25 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-debug/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/img/add-breakpoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-debug/img/add-breakpoint.png -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/img/debug-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-debug/img/debug-it.png -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/img/launch-it.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-debug/img/launch-it.png -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/img/remote-debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-debug/img/remote-debugger.png -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-debug . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-debug 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest-debug . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-debug 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-debug . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-debug 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/main/java/org/acme/GreeterResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.core.MediaType; 7 | 8 | @Path("/hello") 9 | public class GreeterResource { 10 | 11 | @GET 12 | @Produces(MediaType.TEXT_PLAIN) 13 | public String hello() { 14 | return "Hello RESTEasy"; 15 | } 16 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.kubernetes.ingress.expose=true 2 | quarkus.kubernetes.ingress.host=chapter-6-quarkus-rest-debug.127.0.0.1.nip.io 3 | 4 | quarkus.kubernetes.env.vars.quarkus-launch-devmode=true 5 | 6 | quarkus.package.type=mutable-jar 7 | quarkus.live-reload.password=changeit 8 | quarkus.live-reload.url=http://chapter-6-quarkus-rest-debug.127.0.0.1.nip.io/ 9 | 10 | # 11 | # Parameters to be used to expose using NodePort 12 | # the application and socket remote ports: 8080, 5005 using 13 | # the nodeports: 3000, 31000 14 | # quarkus.kubernetes.service-type=NodePort 15 | # quarkus.kubernetes.ports.http.node-port=30000 16 | # quarkus.kubernetes.ports.remote.name=http 17 | # quarkus.kubernetes.ports.remote.host-port=5005 18 | # quarkus.kubernetes.ports.remote.container-port=5005 19 | # quarkus.kubernetes.ports.remote.node-port=31000 20 | 21 | # 22 | # To use the Remote debugging, just un-comment this property to let dekorate to add the ENV VAR to 23 | # the spec part of the Deployment MANIFEST 24 | # 25 | # quarkus.kubernetes.env.vars.JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005 26 | 27 | # Please uncomment me and specify the correct values 28 | # quarkus.container-image.registry=quay.io 29 | # quarkus.container-image.group= 30 | # quarkus.container-image.name=chapter-6-quarkus-rest-debug 31 | # quarkus.container-image.tag=1.0 32 | # quarkus.container-image.build=true 33 | # quarkus.container-image.push=true 34 | # quarkus.container-image.username= 35 | # quarkus.container-image.password= 36 | 37 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/test/java/org/acme/GreeterResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.QuarkusTest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static io.restassured.RestAssured.given; 7 | import static org.hamcrest.CoreMatchers.is; 8 | 9 | @QuarkusTest 10 | public class GreeterResourceTest { 11 | 12 | @Test 13 | public void testHelloEndpoint() { 14 | given() 15 | .when().get("/hello") 16 | .then() 17 | .statusCode(200) 18 | .body(is("Hello RESTEasy")); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-debug/src/test/java/org/acme/NativeGreeterResourceIT.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import io.quarkus.test.junit.NativeImageTest; 4 | 5 | @NativeImageTest 6 | public class NativeGreeterResourceIT extends GreeterResourceTest { 7 | 8 | // Execute the same tests but in native mode. 9 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest-monitoring/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-monitoring . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-monitoring 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest-monitoring . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-monitoring 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest-monitoring . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest-monitoring 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/java/org/acme/FrancophoneService.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.enterprise.context.ApplicationScoped; 4 | import org.eclipse.microprofile.opentracing.Traced; 5 | 6 | @ApplicationScoped 7 | public class FrancophoneService { 8 | 9 | @Traced 10 | public String bonjour() { 11 | return "bonjour"; 12 | } 13 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/java/org/acme/GreetingResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.PathParam; 6 | 7 | import io.micrometer.core.instrument.MeterRegistry; 8 | import io.micrometer.core.instrument.Tags; 9 | 10 | @Path("/hello") 11 | public class GreetingResource { 12 | private final MeterRegistry registry; 13 | 14 | public GreetingResource(MeterRegistry registry) { 15 | this.registry = registry; 16 | } 17 | 18 | @GET 19 | @Path("/{name}") 20 | public String sayHello(@PathParam(value = "name") String name) { 21 | this.registry.counter("greeting_counter", Tags.of("name", name)).increment(); 22 | return "Hello!"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/java/org/acme/TracedResource.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import javax.ws.rs.GET; 4 | import javax.ws.rs.Path; 5 | import javax.ws.rs.Produces; 6 | import javax.ws.rs.client.ClientBuilder; 7 | import javax.ws.rs.core.Context; 8 | import javax.ws.rs.core.MediaType; 9 | import javax.ws.rs.core.UriInfo; 10 | 11 | import io.micrometer.core.annotation.Counted; 12 | import org.jboss.logging.Logger; 13 | import org.jboss.resteasy.reactive.client.impl.UniInvoker; 14 | 15 | import io.smallrye.mutiny.Uni; 16 | 17 | @Path("/traced") 18 | public class TracedResource { 19 | 20 | private static final Logger LOG = Logger.getLogger(TracedResource.class); 21 | private final FrancophoneService exampleBean; 22 | 23 | public TracedResource(FrancophoneService exampleBean) { 24 | this.exampleBean = exampleBean; 25 | } 26 | 27 | @GET 28 | @Path("/hello") 29 | @Produces(MediaType.TEXT_PLAIN) 30 | @Counted 31 | public String hello() { 32 | LOG.info("hello"); 33 | return "hello"; 34 | } 35 | 36 | @GET 37 | @Path("/chain") 38 | @Produces(MediaType.TEXT_PLAIN) 39 | public Uni chain(@Context UriInfo uriInfo) { 40 | return ClientBuilder.newClient() 41 | .target(uriInfo.getBaseUriBuilder().path("/hello/hh")) 42 | .request() 43 | .rx(UniInvoker.class) 44 | .get(String.class) 45 | .onItem().transform(response -> "chain -> " + this.exampleBean.bonjour() + " -> " + response); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.jaeger.service-name=myservice 2 | quarkus.jaeger.sampler-type=const 3 | quarkus.jaeger.sampler-param=1 4 | quarkus.log.console.format=%d{HH:mm:ss} %-5p traceId=%X{traceId}, parentId=%X{parentId}, spanId=%X{spanId}, sampled=%X{sampled} [%c{2.}] (%t) %s%e%n 5 | 6 | # Please uncomment me and specify the correct values 7 | # quarkus.container-image.registry=quay.io 8 | # quarkus.container-image.group= 9 | # quarkus.container-image.name=chapter-6-quarkus-rest-monitoring 10 | # quarkus.container-image.tag=1.0 11 | # quarkus.container-image.build=true 12 | # quarkus.container-image.push=true 13 | # quarkus.container-image.username= 14 | # quarkus.container-image.password= 15 | 16 | 17 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/test/java/org/acme/GreetingResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import static io.restassured.RestAssured.when; 4 | import static org.hamcrest.Matchers.*; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import io.quarkus.test.junit.QuarkusTest; 9 | 10 | import io.restassured.http.ContentType; 11 | 12 | @QuarkusTest 13 | class GreetingResourceTest { 14 | @Test 15 | public void sayHello() { 16 | when() 17 | .get("/hello/hh") 18 | .then() 19 | .statusCode(200) 20 | .contentType(ContentType.TEXT) 21 | .body(is("Hello!")); 22 | 23 | when() 24 | .get("/q/metrics") 25 | .then() 26 | .statusCode(200) 27 | .body(containsString("greeting_counter_total{name=\"hh\"}")); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest-monitoring/src/test/java/org/acme/TracedResourceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme; 2 | 3 | import static io.restassured.RestAssured.given; 4 | import static org.hamcrest.CoreMatchers.is; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | 9 | import io.quarkus.test.junit.QuarkusTest; 10 | import io.quarkus.test.junit.mockito.InjectMock; 11 | 12 | @QuarkusTest 13 | public class TracedResourceTest { 14 | @InjectMock 15 | FrancophoneService francophoneService; 16 | 17 | @Test 18 | public void testHelloEndpoint() { 19 | given() 20 | .when().get("/traced/hello") 21 | .then() 22 | .statusCode(200) 23 | .body(is("hello")); 24 | } 25 | 26 | @Test 27 | public void testChain() { 28 | Mockito.when(this.francophoneService.bonjour()) 29 | .thenReturn("bonjour"); 30 | 31 | given() 32 | .when().get("/traced/chain") 33 | .then() 34 | .statusCode(200) 35 | .body(is("chain -> bonjour -> Hello!")); 36 | 37 | Mockito.verify(this.francophoneService).bonjour(); 38 | Mockito.verifyNoMoreInteractions(this.francophoneService); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/.gitignore: -------------------------------------------------------------------------------- 1 | #Maven 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | release.properties 7 | 8 | # Eclipse 9 | .project 10 | .classpath 11 | .settings/ 12 | bin/ 13 | 14 | # IntelliJ 15 | .idea 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # NetBeans 21 | nb-configuration.xml 22 | 23 | # Visual Studio Code 24 | .vscode 25 | .factorypath 26 | 27 | # OSX 28 | .DS_Store 29 | 30 | # Vim 31 | *.swp 32 | *.swo 33 | 34 | # patch 35 | *.orig 36 | *.rej 37 | 38 | # Local environment 39 | .env 40 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quarkus-for-spring-developers/examples/591093be382163260adf59fef4f9624586c181be/chapter-6/chapter-6-quarkus-rest/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/docker/Dockerfile.native-distroless: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a distroless container that runs the Quarkus application in native (no JVM) mode 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native-distroless -t quarkus/chapter-6-quarkus-rest . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest 15 | # 16 | ### 17 | FROM quay.io/quarkus/quarkus-distroless-image:1.0 18 | COPY target/*-runner /application 19 | 20 | EXPOSE 8080 21 | USER nonroot 22 | 23 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 24 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/chapter-6-quarkus-rest . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/chapter-6-quarkus-rest 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/java/org/acme/domain/CustomRuntimeException.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | public class CustomRuntimeException extends RuntimeException { 4 | public CustomRuntimeException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/java/org/acme/domain/Fruit.java: -------------------------------------------------------------------------------- 1 | package org.acme.domain; 2 | 3 | import javax.validation.constraints.NotBlank; 4 | 5 | public class Fruit { 6 | private String name; 7 | private String description; 8 | 9 | public Fruit(String name, String description) { 10 | this.name = name; 11 | this.description = description; 12 | } 13 | 14 | public Fruit() { 15 | } 16 | 17 | @NotBlank 18 | public String getName() { 19 | return this.name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public String getDescription() { 27 | return this.description; 28 | } 29 | 30 | public void setDescription(String description) { 31 | this.description = description; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/java/org/acme/rest/CustomError.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import java.util.StringJoiner; 4 | 5 | import io.quarkus.runtime.annotations.RegisterForReflection; 6 | 7 | @RegisterForReflection 8 | public class CustomError { 9 | private int errorCode; 10 | private String errorMessage; 11 | 12 | public CustomError() { 13 | } 14 | 15 | public CustomError(int errorCode, String errorMessage) { 16 | this.errorCode = errorCode; 17 | this.errorMessage = errorMessage; 18 | } 19 | 20 | public int getErrorCode() { 21 | return this.errorCode; 22 | } 23 | 24 | public void setErrorCode(int errorCode) { 25 | this.errorCode = errorCode; 26 | } 27 | 28 | public String getErrorMessage() { 29 | return this.errorMessage; 30 | } 31 | 32 | public void setErrorMessage(String errorMessage) { 33 | this.errorMessage = errorMessage; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return new StringJoiner(", ", CustomError.class.getSimpleName() + "[", "]") 39 | .add("errorCode=" + this.errorCode) 40 | .add("errorMessage='" + this.errorMessage + "'") 41 | .toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/java/org/acme/rest/GlobalErrorHandler.java: -------------------------------------------------------------------------------- 1 | package org.acme.rest; 2 | 3 | import javax.ws.rs.core.Response; 4 | 5 | import org.acme.domain.CustomRuntimeException; 6 | import org.jboss.resteasy.reactive.server.ServerExceptionMapper; 7 | 8 | public class GlobalErrorHandler { 9 | @ServerExceptionMapper(CustomRuntimeException.class) 10 | public Response handleCustomRuntimeException(CustomRuntimeException cre) { 11 | return Response.serverError() 12 | .header("X-CUSTOM-ERROR", "500") 13 | .entity(new CustomError(500, cre.getMessage())) 14 | .build(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/java/org/acme/service/SimpleHealthCheck.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 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 javax.enterprise.context.ApplicationScoped; 8 | 9 | @Liveness 10 | @ApplicationScoped 11 | public class SimpleHealthCheck implements HealthCheck { 12 | 13 | @Override 14 | public HealthCheckResponse call() { 15 | return HealthCheckResponse.named("Simple health check") 16 | .withData("foo", "fooValue") 17 | .withData("bar","barValue") 18 | .up() 19 | .build(); 20 | } 21 | } -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # quarkus.kubernetes.ingress.expose=true 2 | # quarkus.kubernetes.ingress.host=chapter-6-quarkus-rest.127.0.0.1.nip.io 3 | 4 | # Please uncomment me and specify the correct values 5 | # quarkus.container-image.registry=quay.io 6 | # quarkus.container-image.group= 7 | # quarkus.container-image.name=chapter-6-quarkus-rest 8 | # quarkus.container-image.tag=1.0 9 | # quarkus.container-image.build=true 10 | # quarkus.container-image.push=true 11 | # quarkus.container-image.username= 12 | # quarkus.container-image.password= 13 | 14 | -------------------------------------------------------------------------------- /chapter-6/chapter-6-quarkus-rest/src/test/java/org/acme/service/FruitServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.acme.service; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import org.acme.domain.Fruit; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; 9 | 10 | class FruitServiceTest { 11 | FruitService fruitService = new FruitService(); 12 | 13 | @Test 14 | public void getFruits() { 15 | assertThat(this.fruitService.getFruits()) 16 | .hasSize(2); 17 | } 18 | 19 | @Test 20 | public void getFruitFound() { 21 | var fruit = this.fruitService.getFruit("Apple") 22 | .subscribe() 23 | .withSubscriber(UniAssertSubscriber.create()) 24 | .assertCompleted() 25 | .getItem(); 26 | 27 | assertThat(fruit) 28 | .isNotNull() 29 | .extracting(Fruit::getName, Fruit::getDescription) 30 | .containsExactly("Apple", "Winter fruit"); 31 | } 32 | 33 | @Test 34 | public void getFruitNotFound() { 35 | this.fruitService.getFruit("Pear") 36 | .subscribe() 37 | .withSubscriber(UniAssertSubscriber.create()) 38 | .assertCompleted() 39 | .assertItem(null); 40 | } 41 | 42 | @Test 43 | public void addFruit() { 44 | assertThat(this.fruitService.addFruit(new Fruit("Pear", "Delicious fruit"))) 45 | .hasSize(3); 46 | } 47 | 48 | @Test 49 | public void delete() { 50 | this.fruitService.deleteFruit("Apple"); 51 | 52 | assertThat(this.fruitService.getFruits()) 53 | .hasSize(1); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /codeready-workspaces/Dockerfile.crw-mandrel: -------------------------------------------------------------------------------- 1 | FROM registry.redhat.io/codeready-workspaces/plugin-java11-rhel8:latest 2 | 3 | USER root 4 | 5 | # Install dependencies for mandrel 6 | RUN dnf install -y glibc-devel zlib-devel gcc glibc-static 7 | 8 | # Get and install mandrel 9 | RUN wget https://github.com/graalvm/mandrel/releases/download/mandrel-21.1.0.0-Final/mandrel-java11-linux-amd64-21.1.0.0-Final.tar.gz && \ 10 | gzip -dc mandrel-java11-linux-amd64-21.1.0.0-Final.tar.gz | tar xvf - && \ 11 | mv mandrel-java11-21.1.0.0-Final /opt && \ 12 | rm -rf mandrel-java11-21.1.0.0-Final 13 | 14 | ENV GRAALVM_HOME=/opt/mandrel-java11-21.1.0.0-Final 15 | 16 | USER jboss 17 | -------------------------------------------------------------------------------- /devfile.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1.0.0 2 | metadata: 3 | generateName: quarkus-spring-devs- 4 | projects: 5 | - name: examples 6 | source: 7 | location: 'https://github.com/quarkus-for-spring-developers/examples.git' 8 | type: git 9 | branch: main 10 | components: 11 | - type: chePlugin 12 | id: redhat/quarkus-java11/latest 13 | - type: dockerimage 14 | alias: maven 15 | image: 'quay.io/edeandrea/plugin-java11-rhel8-mandrel:21.0.0.0.Final-java11' 16 | env: 17 | - name: JAVA_OPTS 18 | value: '-XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Djava.security.egd=file:/dev/./urandom -Duser.home=/home/jboss' 19 | - name: MAVEN_OPTS 20 | value: $(JAVA_OPTS) 21 | memoryLimit: 2560Mi 22 | mountSources: true 23 | volumes: 24 | - name: m2 25 | containerPath: /home/jboss/.m2 26 | endpoints: 27 | - name: application 28 | port: 8080 29 | commands: 30 | - name: Attach remote debugger 31 | actions: 32 | - type: vscode-launch 33 | referenceContent: | 34 | { 35 | "version": "0.2.0", 36 | "configurations": [ 37 | { 38 | "type": "java", 39 | "request": "attach", 40 | "name": "Attach to Remote App", 41 | "hostName": "localhost", 42 | "port": 5005 43 | } 44 | ] 45 | } 46 | --------------------------------------------------------------------------------