+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..6d0ee1c
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..3fc909c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..23fe160
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules/m1_pe_kafka.iml b/.idea/modules/m1_pe_kafka.iml
new file mode 100644
index 0000000..bd41dfe
--- /dev/null
+++ b/.idea/modules/m1_pe_kafka.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Compose.run.xml b/.run/Compose.run.xml
new file mode 100644
index 0000000..fb9dc64
--- /dev/null
+++ b/.run/Compose.run.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/compose.run.xml b/.run/compose.run.xml
deleted file mode 100644
index d9fb245..0000000
--- a/.run/compose.run.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 94d59e9..556aabf 100644
--- a/README.md
+++ b/README.md
@@ -23,26 +23,26 @@ Alternatively, you can build and run the project manually by using
[gradle](https://gradle.org/) and launching the application with [docker-compose](https://docs.docker.com/compose/).
```shell
-gradlew build
+# build the modules
+(cd ./applications/consumer/ && ./gradlew build)
+(cd ./applications/converter/ && ./gradlew build)
+(cd ./applications/producer/ && ./gradlew build)
+# start the docker based on the docker-compose.yaml file
docker compose up
```
## Configuration
-If you wish, you can modify the configuration of the `docker-compose.yaml` file to fit your needs.
+The configuration can be modified in the [docker-compose.yaml](./docker-compose.yaml) file to fit your needs.
-The container `application` can be easily modified with the following environment variables :
-
-| Name | Required | Format | Default | Description |
-|------------------------------|----------|-----------------------------------------------------|--------------------------------------------------------------------|---------------------------------------------------------------|
-| KAFKA_BOOTSTRAP_SERVERS | true | \[:port] | / | The Kafka server address |
-| TOPIC_TEMPERATURE_CELSIUS | false | string of alphanumeric characters, ".", "_" and "-" | temperature-celsius | The name of the Kafka topic for the temperature in Celsius |
-| TOPIC_TEMPERATURE_FAHRENHEIT | false | string of alphanumeric characters, ".", "_" and "-" | temperature-fahrenheit | The name of the Kafka topic for the temperature in Fahrenheit |
-| TEMPERATURE_LOCATION | true | \, \ | 49.9, 2.3 ([Amiens, France](https://fr.wikipedia.org/wiki/Amiens)) | The coordinates where to get the temperatures from |
+You can find a list for each module of the project containing their individual configuration :
+- [producer](./applications/producer/README.md)
+- [consumer](./applications/consumer/README.md)
+- [converter](./applications/converter/README.md)
## Expectation
The `application` container shall print the current temperature at the selected place in Fahrenheit every minute.
-You can also access this value with the REST api at the `http://localhost:8080/temperature` endpoint.
+You can also access this value with the REST api at the `http://localhost:8080/v1/temperature` endpoint.
## References
-The project use the [Open-Meteo API](https://open-meteo.com/) to fetch the current temperature at the
-given location.
\ No newline at end of file
+The project use the [Open-Meteo](https://open-meteo.com/) API to fetch the current temperature at the
+given location.
diff --git a/applications/common/.gitignore b/applications/common/.gitignore
new file mode 100644
index 0000000..216783d
--- /dev/null
+++ b/applications/common/.gitignore
@@ -0,0 +1,39 @@
+# Gradle
+.gradle/
+build/
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+.env
+
+# Plugin directory
+/.quarkus/cli/plugins/
diff --git a/applications/common/README.md b/applications/common/README.md
new file mode 100644
index 0000000..b0defed
--- /dev/null
+++ b/applications/common/README.md
@@ -0,0 +1,15 @@
+# Module: Common
+
+This module contain common code that could be used between the three others modules of the application.
+
+It implements some errors or some useful functionalities and structure to avoid redefining them in all the modules
+and making debugging easier by only modifying a single file to fix the corresponding issue everywhere.
+
+## Build
+
+This project is built automatically when building the others modules.
+
+However, you can still build it manually with the following command :
+```shell
+./gradlew build
+```
diff --git a/build.gradle.kts b/applications/common/build.gradle.kts
similarity index 87%
rename from build.gradle.kts
rename to applications/common/build.gradle.kts
index 1468849..1d82bad 100644
--- a/build.gradle.kts
+++ b/applications/common/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
- kotlin("jvm") version "1.9.23"
- kotlin("plugin.allopen") version "1.9.23"
+ kotlin("jvm") version "2.0.0"
+ kotlin("plugin.allopen") version "2.0.0"
id("io.quarkus")
}
@@ -14,9 +14,6 @@ val quarkusPlatformArtifactId: String by project
val quarkusPlatformVersion: String by project
dependencies {
- // language
- implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
-
// quarkus
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
implementation("io.quarkus:quarkus-kotlin")
@@ -27,7 +24,6 @@ dependencies {
implementation("io.quarkus:quarkus-kafka-client")
implementation("io.quarkus:quarkus-kafka-streams")
implementation("io.quarkus:quarkus-messaging-kafka")
- implementation("io.quarkus:quarkus-rest")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.google.code.gson:gson:2.8.9")
}
diff --git a/applications/common/gradle.properties b/applications/common/gradle.properties
new file mode 100644
index 0000000..a890bb8
--- /dev/null
+++ b/applications/common/gradle.properties
@@ -0,0 +1,6 @@
+#Gradle properties
+quarkusPluginId=io.quarkus
+quarkusPluginVersion=3.12.0
+quarkusPlatformGroupId=io.quarkus.platform
+quarkusPlatformArtifactId=quarkus-bom
+quarkusPlatformVersion=3.12.0
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/applications/common/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from gradle/wrapper/gradle-wrapper.jar
rename to applications/common/gradle/wrapper/gradle-wrapper.jar
diff --git a/gradle/wrapper/gradle-wrapper.properties b/applications/common/gradle/wrapper/gradle-wrapper.properties
similarity index 93%
rename from gradle/wrapper/gradle-wrapper.properties
rename to applications/common/gradle/wrapper/gradle-wrapper.properties
index 17655d0..0d18421 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/applications/common/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/applications/common/gradlew
similarity index 100%
rename from gradlew
rename to applications/common/gradlew
diff --git a/gradlew.bat b/applications/common/gradlew.bat
similarity index 100%
rename from gradlew.bat
rename to applications/common/gradlew.bat
diff --git a/settings.gradle.kts b/applications/common/settings.gradle.kts
similarity index 89%
rename from settings.gradle.kts
rename to applications/common/settings.gradle.kts
index 54afb3f..07e5267 100644
--- a/settings.gradle.kts
+++ b/applications/common/settings.gradle.kts
@@ -10,4 +10,4 @@ pluginManagement {
id(quarkusPluginId) version quarkusPluginVersion
}
}
-rootProject.name = "m1_pe_kafka"
+rootProject.name = "common"
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/error/MissingEnvironmentException.kt b/applications/common/src/main/kotlin/fr/faraphel/common/error/MissingEnvironmentException.kt
similarity index 84%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/error/MissingEnvironmentException.kt
rename to applications/common/src/main/kotlin/fr/faraphel/common/error/MissingEnvironmentException.kt
index 9ea00a1..2d328d6 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/error/MissingEnvironmentException.kt
+++ b/applications/common/src/main/kotlin/fr/faraphel/common/error/MissingEnvironmentException.kt
@@ -1,4 +1,4 @@
-package fr.faraphel.m1_pe_kafka.error
+package fr.faraphel.common.error
/**
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/error/http/HttpException.kt b/applications/common/src/main/kotlin/fr/faraphel/common/error/http/HttpException.kt
similarity index 88%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/error/http/HttpException.kt
rename to applications/common/src/main/kotlin/fr/faraphel/common/error/http/HttpException.kt
index 8ef9daf..4fb47aa 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/error/http/HttpException.kt
+++ b/applications/common/src/main/kotlin/fr/faraphel/common/error/http/HttpException.kt
@@ -1,4 +1,4 @@
-package fr.faraphel.m1_pe_kafka.error.http
+package fr.faraphel.common.error.http
import java.io.IOException
diff --git a/applications/common/src/main/kotlin/fr/faraphel/common/temperature/Temperature.kt b/applications/common/src/main/kotlin/fr/faraphel/common/temperature/Temperature.kt
new file mode 100644
index 0000000..77e567e
--- /dev/null
+++ b/applications/common/src/main/kotlin/fr/faraphel/common/temperature/Temperature.kt
@@ -0,0 +1,21 @@
+package fr.faraphel.common.temperature
+
+
+/**
+ * Represent a temperature
+ * Allow to store temperature and convert the units in an easier way
+ * @param value the value of the temperature (Kelvin)
+ */
+class Temperature(
+ private val value: Double
+) {
+ // convert Temperature to Double (Kelvin)
+ val asKelvin: Double
+ get() = this.value
+ // convert Temperature to Double (Celsius)
+ val asCelsius: Double
+ get() = this.value - 275.15
+ // convert Temperature to Double (Fahrenheit)
+ val asFahrenheit: Double
+ get() = ((this.value - 273.15) * 1.8) + 32
+}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/utils/Temperature.kt b/applications/common/src/main/kotlin/fr/faraphel/common/temperature/conversion.kt
similarity index 53%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/utils/Temperature.kt
rename to applications/common/src/main/kotlin/fr/faraphel/common/temperature/conversion.kt
index c06da1b..a307d59 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/utils/Temperature.kt
+++ b/applications/common/src/main/kotlin/fr/faraphel/common/temperature/conversion.kt
@@ -1,25 +1,6 @@
-package fr.faraphel.m1_pe_kafka.utils
+package fr.faraphel.common.temperature
-/**
- * Represent a temperature
- * Allow to store temperature and convert the units in an easier way
- * @param value the value of the temperature (Kelvin)
- */
-class Temperature(
- private val value: Double
-) {
- // convert Temperature to Double (Kelvin)
- val asKelvin: Double
- get() = this.value
- // convert Temperature to Double (Celsius)
- val asCelsius: Double
- get() = this.value - 275.15
- // convert Temperature to Double (Fahrenheit)
- val asFahrenheit: Double
- get() = ((this.value - 273.15) * 1.8) + 32
-}
-
// convert Double (Kelvin) to Temperature
val Double.kelvin: Temperature
get() = Temperature(this)
diff --git a/applications/common/src/main/resources/application.properties b/applications/common/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/.dockerignore b/applications/consumer/.dockerignore
similarity index 100%
rename from .dockerignore
rename to applications/consumer/.dockerignore
diff --git a/applications/consumer/.gitignore b/applications/consumer/.gitignore
new file mode 100644
index 0000000..216783d
--- /dev/null
+++ b/applications/consumer/.gitignore
@@ -0,0 +1,39 @@
+# Gradle
+.gradle/
+build/
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+.env
+
+# Plugin directory
+/.quarkus/cli/plugins/
diff --git a/applications/consumer/README.md b/applications/consumer/README.md
new file mode 100644
index 0000000..2d16e3e
--- /dev/null
+++ b/applications/consumer/README.md
@@ -0,0 +1,22 @@
+# Module: Consumer
+
+This module contain the code responsible for receiving values, displaying them in the container output and hosting
+the REST API where we can find the latest value received.
+
+## Build
+
+This project is built automatically when using [docker-compose](https://docs.docker.com/compose/) at the root.
+
+However, you can still build it manually with the following command :
+```shell
+./gradlew build
+```
+
+## Configuration
+
+This module can be easily modified with the following environment variables :
+
+| Name | Required | Format | Default | Description |
+|-------------------------|----------|-----------------------------------------------------|---------|---------------------------------------------------|
+| KAFKA_BOOTSTRAP_SERVERS | true | \[:port] | / | The Kafka server address |
+| TEMPERATURE_TOPIC | true | string of alphanumeric characters, ".", "_" and "-" | / | The Kafka topic were the temperature can be found |
diff --git a/applications/consumer/build.gradle.kts b/applications/consumer/build.gradle.kts
new file mode 100644
index 0000000..b937130
--- /dev/null
+++ b/applications/consumer/build.gradle.kts
@@ -0,0 +1,54 @@
+plugins {
+ kotlin("jvm") version "2.0.0"
+ kotlin("plugin.allopen") version "2.0.0"
+ id("io.quarkus")
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+val quarkusPlatformGroupId: String by project
+val quarkusPlatformArtifactId: String by project
+val quarkusPlatformVersion: String by project
+
+dependencies {
+ // common
+ implementation(project(":common"))
+
+ // quarkus
+ implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
+ implementation("io.quarkus:quarkus-kotlin")
+ implementation("io.quarkus:quarkus-arc")
+ testImplementation("io.quarkus:quarkus-junit5")
+ implementation("io.quarkus:quarkus-rest")
+
+ // libraries
+ implementation("io.quarkus:quarkus-kafka-client")
+ implementation("io.quarkus:quarkus-kafka-streams")
+ implementation("com.google.code.gson:gson:2.8.9")
+}
+
+group = "fr.faraphel"
+version = "1.0-SNAPSHOT"
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
+}
+
+tasks.withType {
+ systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
+}
+allOpen {
+ annotation("jakarta.ws.rs.Path")
+ annotation("jakarta.enterprise.context.ApplicationScoped")
+ annotation("jakarta.persistence.Entity")
+ annotation("io.quarkus.test.junit.QuarkusTest")
+}
+
+tasks.withType {
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_21.toString()
+ kotlinOptions.javaParameters = true
+}
diff --git a/applications/consumer/gradle.properties b/applications/consumer/gradle.properties
new file mode 100644
index 0000000..a890bb8
--- /dev/null
+++ b/applications/consumer/gradle.properties
@@ -0,0 +1,6 @@
+#Gradle properties
+quarkusPluginId=io.quarkus
+quarkusPluginVersion=3.12.0
+quarkusPlatformGroupId=io.quarkus.platform
+quarkusPlatformArtifactId=quarkus-bom
+quarkusPlatformVersion=3.12.0
\ No newline at end of file
diff --git a/applications/consumer/gradle/wrapper/gradle-wrapper.jar b/applications/consumer/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
Binary files /dev/null and b/applications/consumer/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/applications/consumer/gradle/wrapper/gradle-wrapper.properties b/applications/consumer/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0d18421
--- /dev/null
+++ b/applications/consumer/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/applications/consumer/gradlew b/applications/consumer/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/applications/consumer/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/applications/consumer/gradlew.bat b/applications/consumer/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/applications/consumer/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/applications/consumer/settings.gradle.kts b/applications/consumer/settings.gradle.kts
new file mode 100644
index 0000000..6624e23
--- /dev/null
+++ b/applications/consumer/settings.gradle.kts
@@ -0,0 +1,17 @@
+pluginManagement {
+ val quarkusPluginVersion: String by settings
+ val quarkusPluginId: String by settings
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ mavenLocal()
+ }
+ plugins {
+ id(quarkusPluginId) version quarkusPluginVersion
+ }
+}
+rootProject.name = "consumer"
+
+// Dependencies
+include(":common")
+project(":common").projectDir = file("../common")
\ No newline at end of file
diff --git a/src/main/docker/Dockerfile.jvm b/applications/consumer/src/main/docker/Dockerfile.jvm
similarity index 95%
rename from src/main/docker/Dockerfile.jvm
rename to applications/consumer/src/main/docker/Dockerfile.jvm
index e1f276f..ac50743 100644
--- a/src/main/docker/Dockerfile.jvm
+++ b/applications/consumer/src/main/docker/Dockerfile.jvm
@@ -7,11 +7,11 @@
#
# Then, build the image with:
#
-# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/m1_pe_kafka-jvm .
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/consumer-jvm .
#
# Then run the container using:
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka-jvm
+# docker run -i --rm -p 8080:8080 quarkus/consumer-jvm
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
@@ -20,7 +20,7 @@
#
# Then run the container using :
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka-jvm
+# docker run -i --rm -p 8080:8080 quarkus/consumer-jvm
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
@@ -77,7 +77,7 @@
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
-FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
ENV LANGUAGE='en_US:en'
diff --git a/src/main/docker/Dockerfile.legacy-jar b/applications/consumer/src/main/docker/Dockerfile.legacy-jar
similarity index 95%
rename from src/main/docker/Dockerfile.legacy-jar
rename to applications/consumer/src/main/docker/Dockerfile.legacy-jar
index 694acea..68f547f 100644
--- a/src/main/docker/Dockerfile.legacy-jar
+++ b/applications/consumer/src/main/docker/Dockerfile.legacy-jar
@@ -7,11 +7,11 @@
#
# Then, build the image with:
#
-# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/m1_pe_kafka-legacy-jar .
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/consumer-legacy-jar .
#
# Then run the container using:
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka-legacy-jar
+# docker run -i --rm -p 8080:8080 quarkus/consumer-legacy-jar
#
# If you want to include the debug port into your docker image
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
@@ -20,7 +20,7 @@
#
# Then run the container using :
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka-legacy-jar
+# docker run -i --rm -p 8080:8080 quarkus/consumer-legacy-jar
#
# This image uses the `run-java.sh` script to run the application.
# This scripts computes the command line to execute your Java application, and
@@ -77,7 +77,7 @@
# accessed directly. (example: "foo.example.com,bar.example.com")
#
###
-FROM registry.access.redhat.com/ubi8/openjdk-21:1.18
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
ENV LANGUAGE='en_US:en'
diff --git a/src/main/docker/Dockerfile.native b/applications/consumer/src/main/docker/Dockerfile.native
similarity index 81%
rename from src/main/docker/Dockerfile.native
rename to applications/consumer/src/main/docker/Dockerfile.native
index 2071597..c4c22fd 100644
--- a/src/main/docker/Dockerfile.native
+++ b/applications/consumer/src/main/docker/Dockerfile.native
@@ -7,11 +7,11 @@
#
# Then, build the image with:
#
-# docker build -f src/main/docker/Dockerfile.native -t quarkus/m1_pe_kafka .
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/consumer .
#
# Then run the container using:
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka
+# docker run -i --rm -p 8080:8080 quarkus/consumer
#
###
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
diff --git a/src/main/docker/Dockerfile.native-micro b/applications/consumer/src/main/docker/Dockerfile.native-micro
similarity index 92%
rename from src/main/docker/Dockerfile.native-micro
rename to applications/consumer/src/main/docker/Dockerfile.native-micro
index 703e0d6..47dbda9 100644
--- a/src/main/docker/Dockerfile.native-micro
+++ b/applications/consumer/src/main/docker/Dockerfile.native-micro
@@ -10,11 +10,11 @@
#
# Then, build the image with:
#
-# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/m1_pe_kafka .
+# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/consumer .
#
# Then run the container using:
#
-# docker run -i --rm -p 8080:8080 quarkus/m1_pe_kafka
+# docker run -i --rm -p 8080:8080 quarkus/consumer
#
###
FROM quay.io/quarkus/quarkus-micro-image:2.0
diff --git a/applications/consumer/src/main/kotlin/fr/faraphel/consumer/Main.kt b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/Main.kt
new file mode 100644
index 0000000..39c1709
--- /dev/null
+++ b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/Main.kt
@@ -0,0 +1,41 @@
+package fr.faraphel.consumer
+
+import fr.faraphel.consumer.kafka.Consumer
+import fr.faraphel.consumer.rest.TemperatureEndpoint
+import fr.faraphel.common.error.MissingEnvironmentException
+import io.quarkus.runtime.QuarkusApplication
+import io.quarkus.runtime.annotations.QuarkusMain
+import java.time.Instant
+
+
+@QuarkusMain
+class Main : QuarkusApplication {
+ override fun run(vararg args: String?): Int {
+ // get the kafka server address
+ val kafkaServer = System.getenv("KAFKA_BOOTSTRAP_SERVERS")
+ ?: throw MissingEnvironmentException("KAFKA_BOOTSTRAP_SERVERS")
+
+ // get the topic name
+ val topicTemperature: String = System.getenv("TEMPERATURE_TOPIC")
+ ?: throw MissingEnvironmentException("TEMPERATURE_TOPIC")
+
+ // create a consumer that will print the received values in the input topic
+ val consumer = Consumer(
+ server=kafkaServer,
+ topic=topicTemperature
+ ) { message ->
+ // clean the values in the record
+ val messageInstant = Instant.ofEpochMilli(message.timestamp())
+ val messageContent = message.value()
+ // print the value
+ println("[${timeFormatter.format(messageInstant)}] ${messageContent}°F")
+ // update the value for the API
+ TemperatureEndpoint.updateData(messageContent, messageInstant)
+ }
+
+ // indefinitely consume new values
+ consumer.consumeForever()
+
+ return 0
+ }
+}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Consumer.kt b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/kafka/Consumer.kt
similarity index 88%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Consumer.kt
rename to applications/consumer/src/main/kotlin/fr/faraphel/consumer/kafka/Consumer.kt
index ba318f8..dcf9139 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Consumer.kt
+++ b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/kafka/Consumer.kt
@@ -1,5 +1,4 @@
-package fr.faraphel.m1_pe_kafka.kafka
-
+package fr.faraphel.consumer.kafka
import org.apache.kafka.clients.consumer.*
import org.apache.kafka.common.serialization.DoubleDeserializer
@@ -21,10 +20,10 @@ import kotlin.time.toJavaDuration
*/
class Consumer(
private val server: String,
- private val identifier: String = "consumer",
+ private val identifier: String = "fr/faraphel/consumer",
private val topic: String,
private val callback: (ConsumerRecord) -> Unit,
-) : Thread() {
+) {
private val properties: Properties = Properties().apply {
// identifier
this[ConsumerConfig.GROUP_ID_CONFIG] = identifier
@@ -49,7 +48,7 @@ class Consumer(
val messages: ConsumerRecords = this.consumer.poll(timeout)
// print them with their timestamp and content
- messages.forEach { message -> this.callback(message) }
+ messages.forEach(this.callback)
}
/**
@@ -59,10 +58,4 @@ class Consumer(
fun consumeForever(timeout: Duration = 1.seconds.toJavaDuration()) {
while (true) this.consume(timeout)
}
-
- /**
- * Thread entrypoint
- * @see consumeForever
- */
- override fun run() = this.consumeForever()
}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/PingEndpoint.kt b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/PingEndpoint.kt
similarity index 86%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/PingEndpoint.kt
rename to applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/PingEndpoint.kt
index 03a93b8..10a73d4 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/PingEndpoint.kt
+++ b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/PingEndpoint.kt
@@ -1,4 +1,4 @@
-package fr.faraphel.m1_pe_kafka.rest
+package fr.faraphel.consumer.rest
import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
@@ -9,7 +9,7 @@ import jakarta.ws.rs.Path
* Always answer "Pong!"
* Can be used to test if the API can be reached
*/
-@Path("ping")
+@Path("/v1/ping")
class PingEndpoint {
/**
* Handler for a GET request on this endpoint
diff --git a/applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/TemperatureEndpoint.kt b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/TemperatureEndpoint.kt
new file mode 100644
index 0000000..c80bca4
--- /dev/null
+++ b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/rest/TemperatureEndpoint.kt
@@ -0,0 +1,43 @@
+package fr.faraphel.consumer.rest
+
+import com.google.gson.Gson
+import jakarta.ws.rs.GET
+import jakarta.ws.rs.Path
+import jakarta.ws.rs.Produces
+import jakarta.ws.rs.core.MediaType
+import java.time.Instant
+
+
+/**
+ * This API endpoint return the latest temperature measured (in Fahrenheit)
+ */
+@Path("/v1/temperature")
+class TemperatureEndpoint {
+ companion object {
+ val jsonParser = Gson() ///< the json parser
+
+ private var temperature: Double? = null ///< the latest temperature value
+ private var time: Instant? = null /// < the time of the latest value
+
+ /**
+ * Setter to update the latest temperature value
+ * @param temperature the new temperature value
+ */
+ fun updateData(temperature: Double, time: Instant) {
+ this.temperature = temperature
+ this.time = time
+ }
+ }
+
+ /**
+ * Handler for a GET request on this endpoint
+ * @return the latest temperature measured
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ fun get(): String = jsonParser.toJson(mapOf(
+ "value" to temperature,
+ "time" to time?.toEpochMilli(),
+ "unit" to "Fahrenheit"
+ ))
+}
diff --git a/applications/consumer/src/main/kotlin/fr/faraphel/consumer/utils.kt b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/utils.kt
new file mode 100644
index 0000000..f310d7b
--- /dev/null
+++ b/applications/consumer/src/main/kotlin/fr/faraphel/consumer/utils.kt
@@ -0,0 +1,10 @@
+package fr.faraphel.consumer
+
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+
+
+// create a time formatter
+val timeFormatter: DateTimeFormatter = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
+ .withZone(ZoneId.systemDefault())
diff --git a/applications/consumer/src/main/resources/application.properties b/applications/consumer/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/applications/converter/.dockerignore b/applications/converter/.dockerignore
new file mode 100644
index 0000000..4361d2f
--- /dev/null
+++ b/applications/converter/.dockerignore
@@ -0,0 +1,5 @@
+*
+!build/*-runner
+!build/*-runner.jar
+!build/lib/*
+!build/quarkus-app/*
\ No newline at end of file
diff --git a/applications/converter/.gitignore b/applications/converter/.gitignore
new file mode 100644
index 0000000..216783d
--- /dev/null
+++ b/applications/converter/.gitignore
@@ -0,0 +1,39 @@
+# Gradle
+.gradle/
+build/
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+.env
+
+# Plugin directory
+/.quarkus/cli/plugins/
diff --git a/applications/converter/README.md b/applications/converter/README.md
new file mode 100644
index 0000000..617d602
--- /dev/null
+++ b/applications/converter/README.md
@@ -0,0 +1,23 @@
+# Module: Converter
+
+This module contain the code responsible for receiving values from a topic, converting them from
+Celsius temperature to Fahrenheit and send it back into another topic.
+
+## Build
+
+This project is built automatically when using [docker-compose](https://docs.docker.com/compose/) at the root.
+
+However, you can still build it manually with the following command :
+```shell
+./gradlew build
+```
+
+## Configuration
+
+This module can be easily modified with the following environment variables :
+
+| Name | Required | Format | Default | Description |
+|------------------------------|----------|-----------------------------------------------------|---------|-----------------------------------------------------------------|
+| KAFKA_BOOTSTRAP_SERVERS | true | \[:port] | / | The Kafka server address |
+| TEMPERATURE_CELSIUS_TOPIC | true | string of alphanumeric characters, ".", "_" and "-" | / | The Kafka topic were the temperature in celsius can be found |
+| TEMPERATURE_FAHRENHEIT_TOPIC | true | string of alphanumeric characters, ".", "_" and "-" | / | The Kafka topic were the temperature in fahrenheit can be found |
diff --git a/applications/converter/build.gradle.kts b/applications/converter/build.gradle.kts
new file mode 100644
index 0000000..3d80c06
--- /dev/null
+++ b/applications/converter/build.gradle.kts
@@ -0,0 +1,52 @@
+plugins {
+ kotlin("jvm") version "2.0.0"
+ kotlin("plugin.allopen") version "2.0.0"
+ id("io.quarkus")
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+val quarkusPlatformGroupId: String by project
+val quarkusPlatformArtifactId: String by project
+val quarkusPlatformVersion: String by project
+
+dependencies {
+ // common
+ implementation(project(":common"))
+
+ // quarkus
+ implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
+ implementation("io.quarkus:quarkus-kotlin")
+ implementation("io.quarkus:quarkus-arc")
+ testImplementation("io.quarkus:quarkus-junit5")
+
+ // libraries
+ implementation("io.quarkus:quarkus-kafka-client")
+ implementation("io.quarkus:quarkus-kafka-streams")
+}
+
+group = "fr.faraphel"
+version = "1.0-SNAPSHOT"
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
+}
+
+tasks.withType {
+ systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
+}
+allOpen {
+ annotation("jakarta.ws.rs.Path")
+ annotation("jakarta.enterprise.context.ApplicationScoped")
+ annotation("jakarta.persistence.Entity")
+ annotation("io.quarkus.test.junit.QuarkusTest")
+}
+
+tasks.withType {
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_21.toString()
+ kotlinOptions.javaParameters = true
+}
\ No newline at end of file
diff --git a/applications/converter/gradle.properties b/applications/converter/gradle.properties
new file mode 100644
index 0000000..a890bb8
--- /dev/null
+++ b/applications/converter/gradle.properties
@@ -0,0 +1,6 @@
+#Gradle properties
+quarkusPluginId=io.quarkus
+quarkusPluginVersion=3.12.0
+quarkusPlatformGroupId=io.quarkus.platform
+quarkusPlatformArtifactId=quarkus-bom
+quarkusPlatformVersion=3.12.0
\ No newline at end of file
diff --git a/applications/converter/gradle/wrapper/gradle-wrapper.jar b/applications/converter/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
Binary files /dev/null and b/applications/converter/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/applications/converter/gradle/wrapper/gradle-wrapper.properties b/applications/converter/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0d18421
--- /dev/null
+++ b/applications/converter/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/applications/converter/gradlew b/applications/converter/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/applications/converter/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/applications/converter/gradlew.bat b/applications/converter/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/applications/converter/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/applications/converter/settings.gradle.kts b/applications/converter/settings.gradle.kts
new file mode 100644
index 0000000..4e76cdd
--- /dev/null
+++ b/applications/converter/settings.gradle.kts
@@ -0,0 +1,17 @@
+pluginManagement {
+ val quarkusPluginVersion: String by settings
+ val quarkusPluginId: String by settings
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ mavenLocal()
+ }
+ plugins {
+ id(quarkusPluginId) version quarkusPluginVersion
+ }
+}
+rootProject.name = "converter"
+
+// Dependencies
+include(":common")
+project(":common").projectDir = file("../common")
\ No newline at end of file
diff --git a/applications/converter/src/main/docker/Dockerfile.jvm b/applications/converter/src/main/docker/Dockerfile.jvm
new file mode 100644
index 0000000..ed5c8f4
--- /dev/null
+++ b/applications/converter/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,96 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./gradlew build
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/converter-jvm .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter-jvm
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter-jvm
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+# in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+# used to calculate a default maximal heap memory based on a containers restriction.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+# of the container available memory as set here. The default is `50` which means 50%
+# of the available memory is used as an upper boundary. You can skip this mechanism by
+# setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+# is used to calculate a default initial heap memory based on the maximum heap memory.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+# is used as the initial heap size. You can skip this mechanism by setting this value
+# to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+# This is used to calculate the maximum value of the initial heap memory. If used in
+# a container without any memory constraints for the container then this option has
+# no effect. If there is a memory constraint then `-Xms` is limited to the value set
+# here. The default is 4096MB which means the calculated value of `-Xms` never will
+# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+# when things are happening. This option, if set to true, will set
+# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+# true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+# (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+# (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+# (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+# previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+# contain the necessary JRE command-line options to specify the required GC, which
+# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+# accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
+
+ENV LANGUAGE='en_US:en'
+
+
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --chown=185 build/quarkus-app/lib/ /deployments/lib/
+COPY --chown=185 build/quarkus-app/*.jar /deployments/
+COPY --chown=185 build/quarkus-app/app/ /deployments/app/
+COPY --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
+
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
+
diff --git a/applications/converter/src/main/docker/Dockerfile.legacy-jar b/applications/converter/src/main/docker/Dockerfile.legacy-jar
new file mode 100644
index 0000000..48a29d2
--- /dev/null
+++ b/applications/converter/src/main/docker/Dockerfile.legacy-jar
@@ -0,0 +1,92 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.package.jar.type=legacy-jar
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/converter-legacy-jar .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter-legacy-jar
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter-legacy-jar
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+# in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+# used to calculate a default maximal heap memory based on a containers restriction.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+# of the container available memory as set here. The default is `50` which means 50%
+# of the available memory is used as an upper boundary. You can skip this mechanism by
+# setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+# is used to calculate a default initial heap memory based on the maximum heap memory.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+# is used as the initial heap size. You can skip this mechanism by setting this value
+# to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+# This is used to calculate the maximum value of the initial heap memory. If used in
+# a container without any memory constraints for the container then this option has
+# no effect. If there is a memory constraint then `-Xms` is limited to the value set
+# here. The default is 4096MB which means the calculated value of `-Xms` never will
+# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+# when things are happening. This option, if set to true, will set
+# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+# true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+# (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+# (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+# (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+# previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+# contain the necessary JRE command-line options to specify the required GC, which
+# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+# accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
+
+ENV LANGUAGE='en_US:en'
+
+
+COPY build/lib/* /deployments/lib/
+COPY build/*-runner.jar /deployments/quarkus-run.jar
+
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
diff --git a/applications/converter/src/main/docker/Dockerfile.native b/applications/converter/src/main/docker/Dockerfile.native
new file mode 100644
index 0000000..3eb1273
--- /dev/null
+++ b/applications/converter/src/main/docker/Dockerfile.native
@@ -0,0 +1,26 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.native.enabled=true
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/converter .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root build/*-runner /work/application
+
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/applications/converter/src/main/docker/Dockerfile.native-micro b/applications/converter/src/main/docker/Dockerfile.native-micro
new file mode 100644
index 0000000..f72e5ac
--- /dev/null
+++ b/applications/converter/src/main/docker/Dockerfile.native-micro
@@ -0,0 +1,29 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+# It uses a micro base image, tuned for Quarkus native executables.
+# It reduces the size of the resulting container image.
+# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.native.enabled=true
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/converter .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/converter
+#
+###
+FROM quay.io/quarkus/quarkus-micro-image:2.0
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root build/*-runner /work/application
+
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/applications/converter/src/main/kotlin/fr/faraphel/converter/Main.kt b/applications/converter/src/main/kotlin/fr/faraphel/converter/Main.kt
new file mode 100644
index 0000000..7bc5ce2
--- /dev/null
+++ b/applications/converter/src/main/kotlin/fr/faraphel/converter/Main.kt
@@ -0,0 +1,41 @@
+package fr.faraphel.converter
+
+import fr.faraphel.common.error.MissingEnvironmentException
+import fr.faraphel.common.temperature.celsius
+import fr.faraphel.converter.kafka.Converter
+import io.quarkus.runtime.QuarkusApplication
+import io.quarkus.runtime.annotations.QuarkusMain
+import kotlin.time.Duration.Companion.seconds
+import kotlin.time.toJavaDuration
+
+
+@QuarkusMain
+class Main : QuarkusApplication {
+ override fun run(vararg args: String?): Int {
+ // get the kafka server address
+ val kafkaServer = System.getenv("KAFKA_BOOTSTRAP_SERVERS")
+ ?: throw MissingEnvironmentException("KAFKA_BOOTSTRAP_SERVERS")
+
+ // get the topics names
+ val topicTemperatureCelsius: String = System.getenv("TEMPERATURE_CELSIUS_TOPIC")
+ ?: throw MissingEnvironmentException("TEMPERATURE_CELSIUS_TOPIC")
+ val topicTemperatureFahrenheit: String = System.getenv("TEMPERATURE_FAHRENHEIT_TOPIC")
+ ?: throw MissingEnvironmentException("TEMPERATURE_FAHRENHEIT_TOPIC")
+
+ // create a converter that will convert values coming from the Celsius topic to the Fahrenheit topic
+ val converter = Converter(
+ server=kafkaServer,
+ inputTopic=topicTemperatureCelsius,
+ outputTopic=topicTemperatureFahrenheit,
+ ) { temperature -> temperature.celsius.asFahrenheit }
+
+ // start converting values
+ converter.start()
+
+ // indefinitely wait for the converter to be paused
+ while (!converter.isPaused)
+ Thread.sleep(1.seconds.toJavaDuration())
+
+ return 0
+ }
+}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Converter.kt b/applications/converter/src/main/kotlin/fr/faraphel/converter/kafka/Converter.kt
similarity index 85%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Converter.kt
rename to applications/converter/src/main/kotlin/fr/faraphel/converter/kafka/Converter.kt
index 6b8bec3..74136cb 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/Converter.kt
+++ b/applications/converter/src/main/kotlin/fr/faraphel/converter/kafka/Converter.kt
@@ -1,4 +1,4 @@
-package fr.faraphel.m1_pe_kafka.kafka
+package fr.faraphel.converter.kafka
import org.apache.kafka.common.serialization.Serdes
import org.apache.kafka.streams.KafkaStreams
@@ -19,7 +19,7 @@ import java.util.*
*/
class Converter(
private val server: String,
- private val identifier: String = "converter",
+ private val identifier: String = "fr/faraphel/converter",
private val inputTopic: String,
private val outputTopic: String,
private val converter: (Double) -> Double,
@@ -54,23 +54,30 @@ class Converter(
* Start the conversion process
* @see KafkaStreams.start
*/
- fun start() = streams.start()
+ fun start() = this.streams.start()
/**
* Pause the conversion process
* @see KafkaStreams.pause
*/
- fun pause() = streams.pause()
+ fun pause() = this.streams.pause()
/**
* Resume a paused conversion process
* @see KafkaStreams.resume
*/
- fun resume() = streams.resume()
+ fun resume() = this.streams.resume()
/**
* Stop a conversion process
* @see KafkaStreams.close
*/
- fun stop() = streams.close()
+ fun stop() = this.streams.close()
+
+ /**
+ * Indicate if the conversion is paused
+ * @see KafkaStreams.isPaused
+ */
+ val isPaused: Boolean
+ get() = this.streams.isPaused
}
diff --git a/applications/converter/src/main/resources/application.properties b/applications/converter/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/applications/producer/.dockerignore b/applications/producer/.dockerignore
new file mode 100644
index 0000000..4361d2f
--- /dev/null
+++ b/applications/producer/.dockerignore
@@ -0,0 +1,5 @@
+*
+!build/*-runner
+!build/*-runner.jar
+!build/lib/*
+!build/quarkus-app/*
\ No newline at end of file
diff --git a/applications/producer/.gitignore b/applications/producer/.gitignore
new file mode 100644
index 0000000..216783d
--- /dev/null
+++ b/applications/producer/.gitignore
@@ -0,0 +1,39 @@
+# Gradle
+.gradle/
+build/
+
+# Eclipse
+.project
+.classpath
+.settings/
+bin/
+
+# IntelliJ
+.idea
+*.ipr
+*.iml
+*.iws
+
+# NetBeans
+nb-configuration.xml
+
+# Visual Studio Code
+.vscode
+.factorypath
+
+# OSX
+.DS_Store
+
+# Vim
+*.swp
+*.swo
+
+# patch
+*.orig
+*.rej
+
+# Local environment
+.env
+
+# Plugin directory
+/.quarkus/cli/plugins/
diff --git a/applications/producer/README.md b/applications/producer/README.md
new file mode 100644
index 0000000..4ec7f47
--- /dev/null
+++ b/applications/producer/README.md
@@ -0,0 +1,23 @@
+# Module: Converter
+
+This module contain the code responsible for getting the current temperature at a given location thanks to the
+[Open-Meteo](https://open-meteo.com/) API, and sending it into a topic.
+
+## Build
+
+This project is built automatically when using [docker-compose](https://docs.docker.com/compose/) at the root.
+
+However, you can still build it manually with the following command :
+```shell
+./gradlew build
+```
+
+## Configuration
+
+This module can be easily modified with the following environment variables :
+
+| Name | Required | Format | Default | Description |
+|-------------------------|----------|-----------------------------------------------------|---------|--------------------------------------------------------------|
+| KAFKA_BOOTSTRAP_SERVERS | true | \[:port] | / | The Kafka server address |
+| TEMPERATURE_TOPIC | true | string of alphanumeric characters, ".", "_" and "-" | / | The Kafka topic were the temperature in celsius can be found |
+| TEMPERATURE_LOCATION | true | \, \ | / | The coordinates where to get the temperatures from |
diff --git a/applications/producer/build.gradle.kts b/applications/producer/build.gradle.kts
new file mode 100644
index 0000000..3be7424
--- /dev/null
+++ b/applications/producer/build.gradle.kts
@@ -0,0 +1,53 @@
+plugins {
+ kotlin("jvm") version "2.0.0"
+ kotlin("plugin.allopen") version "2.0.0"
+ id("io.quarkus")
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+}
+
+val quarkusPlatformGroupId: String by project
+val quarkusPlatformArtifactId: String by project
+val quarkusPlatformVersion: String by project
+
+dependencies {
+ // common
+ implementation(project(":common"))
+
+ // quarkus
+ implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
+ implementation("io.quarkus:quarkus-kotlin")
+ implementation("io.quarkus:quarkus-arc")
+ testImplementation("io.quarkus:quarkus-junit5")
+
+ // libraries
+ implementation("io.quarkus:quarkus-kafka-client")
+ implementation("com.squareup.okhttp3:okhttp:4.12.0")
+ implementation("com.google.code.gson:gson:2.8.9")
+}
+
+group = "fr.faraphel"
+version = "1.0-SNAPSHOT"
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_21
+ targetCompatibility = JavaVersion.VERSION_21
+}
+
+tasks.withType {
+ systemProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager")
+}
+allOpen {
+ annotation("jakarta.ws.rs.Path")
+ annotation("jakarta.enterprise.context.ApplicationScoped")
+ annotation("jakarta.persistence.Entity")
+ annotation("io.quarkus.test.junit.QuarkusTest")
+}
+
+tasks.withType {
+ kotlinOptions.jvmTarget = JavaVersion.VERSION_21.toString()
+ kotlinOptions.javaParameters = true
+}
diff --git a/applications/producer/gradle.properties b/applications/producer/gradle.properties
new file mode 100644
index 0000000..a890bb8
--- /dev/null
+++ b/applications/producer/gradle.properties
@@ -0,0 +1,6 @@
+#Gradle properties
+quarkusPluginId=io.quarkus
+quarkusPluginVersion=3.12.0
+quarkusPlatformGroupId=io.quarkus.platform
+quarkusPlatformArtifactId=quarkus-bom
+quarkusPlatformVersion=3.12.0
\ No newline at end of file
diff --git a/applications/producer/gradle/wrapper/gradle-wrapper.jar b/applications/producer/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
Binary files /dev/null and b/applications/producer/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/applications/producer/gradle/wrapper/gradle-wrapper.properties b/applications/producer/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0d18421
--- /dev/null
+++ b/applications/producer/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/applications/producer/gradlew b/applications/producer/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/applications/producer/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/applications/producer/gradlew.bat b/applications/producer/gradlew.bat
new file mode 100644
index 0000000..5093609
--- /dev/null
+++ b/applications/producer/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/applications/producer/settings.gradle.kts b/applications/producer/settings.gradle.kts
new file mode 100644
index 0000000..ec510df
--- /dev/null
+++ b/applications/producer/settings.gradle.kts
@@ -0,0 +1,17 @@
+pluginManagement {
+ val quarkusPluginVersion: String by settings
+ val quarkusPluginId: String by settings
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ mavenLocal()
+ }
+ plugins {
+ id(quarkusPluginId) version quarkusPluginVersion
+ }
+}
+rootProject.name = "producer"
+
+// Dependencies
+include(":common")
+project(":common").projectDir = file("../common")
\ No newline at end of file
diff --git a/applications/producer/src/main/docker/Dockerfile.jvm b/applications/producer/src/main/docker/Dockerfile.jvm
new file mode 100644
index 0000000..21a8dd0
--- /dev/null
+++ b/applications/producer/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,96 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./gradlew build
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/producer-jvm .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer-jvm
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer-jvm
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+# in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+# used to calculate a default maximal heap memory based on a containers restriction.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+# of the container available memory as set here. The default is `50` which means 50%
+# of the available memory is used as an upper boundary. You can skip this mechanism by
+# setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+# is used to calculate a default initial heap memory based on the maximum heap memory.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+# is used as the initial heap size. You can skip this mechanism by setting this value
+# to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+# This is used to calculate the maximum value of the initial heap memory. If used in
+# a container without any memory constraints for the container then this option has
+# no effect. If there is a memory constraint then `-Xms` is limited to the value set
+# here. The default is 4096MB which means the calculated value of `-Xms` never will
+# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+# when things are happening. This option, if set to true, will set
+# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+# true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+# (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+# (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+# (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+# previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+# contain the necessary JRE command-line options to specify the required GC, which
+# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+# accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
+
+ENV LANGUAGE='en_US:en'
+
+
+# We make four distinct layers so if there are application changes the library layers can be re-used
+COPY --chown=185 build/quarkus-app/lib/ /deployments/lib/
+COPY --chown=185 build/quarkus-app/*.jar /deployments/
+COPY --chown=185 build/quarkus-app/app/ /deployments/app/
+COPY --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
+
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
+
diff --git a/applications/producer/src/main/docker/Dockerfile.legacy-jar b/applications/producer/src/main/docker/Dockerfile.legacy-jar
new file mode 100644
index 0000000..05e7f0a
--- /dev/null
+++ b/applications/producer/src/main/docker/Dockerfile.legacy-jar
@@ -0,0 +1,92 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.package.jar.type=legacy-jar
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/producer-legacy-jar .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer-legacy-jar
+#
+# If you want to include the debug port into your docker image
+# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
+# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
+# when running the container
+#
+# Then run the container using :
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer-legacy-jar
+#
+# This image uses the `run-java.sh` script to run the application.
+# This scripts computes the command line to execute your Java application, and
+# includes memory/GC tuning.
+# You can configure the behavior using the following environment properties:
+# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class")
+# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
+# in JAVA_OPTS (example: "-Dsome.property=foo")
+# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
+# used to calculate a default maximal heap memory based on a containers restriction.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
+# of the container available memory as set here. The default is `50` which means 50%
+# of the available memory is used as an upper boundary. You can skip this mechanism by
+# setting this value to `0` in which case no `-Xmx` option is added.
+# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
+# is used to calculate a default initial heap memory based on the maximum heap memory.
+# If used in a container without any memory constraints for the container then this
+# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
+# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
+# is used as the initial heap size. You can skip this mechanism by setting this value
+# to `0` in which case no `-Xms` option is added (example: "25")
+# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
+# This is used to calculate the maximum value of the initial heap memory. If used in
+# a container without any memory constraints for the container then this option has
+# no effect. If there is a memory constraint then `-Xms` is limited to the value set
+# here. The default is 4096MB which means the calculated value of `-Xms` never will
+# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
+# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
+# when things are happening. This option, if set to true, will set
+# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
+# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
+# true").
+# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
+# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
+# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
+# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
+# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
+# (example: "20")
+# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
+# (example: "40")
+# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
+# (example: "4")
+# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
+# previous GC times. (example: "90")
+# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
+# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
+# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
+# contain the necessary JRE command-line options to specify the required GC, which
+# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
+# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
+# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
+# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
+# accessed directly. (example: "foo.example.com,bar.example.com")
+#
+###
+FROM registry.access.redhat.com/ubi8/openjdk-21:1.19
+
+ENV LANGUAGE='en_US:en'
+
+
+COPY build/lib/* /deployments/lib/
+COPY build/*-runner.jar /deployments/quarkus-run.jar
+
+USER 185
+ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
+
+ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
diff --git a/applications/producer/src/main/docker/Dockerfile.native b/applications/producer/src/main/docker/Dockerfile.native
new file mode 100644
index 0000000..87366bd
--- /dev/null
+++ b/applications/producer/src/main/docker/Dockerfile.native
@@ -0,0 +1,26 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.native.enabled=true
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/producer .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root build/*-runner /work/application
+
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/applications/producer/src/main/docker/Dockerfile.native-micro b/applications/producer/src/main/docker/Dockerfile.native-micro
new file mode 100644
index 0000000..62a9c2f
--- /dev/null
+++ b/applications/producer/src/main/docker/Dockerfile.native-micro
@@ -0,0 +1,29 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
+# It uses a micro base image, tuned for Quarkus native executables.
+# It reduces the size of the resulting container image.
+# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
+#
+# Before building the container image run:
+#
+# ./gradlew build -Dquarkus.native.enabled=true
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/producer .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/producer
+#
+###
+FROM quay.io/quarkus/quarkus-micro-image:2.0
+WORKDIR /work/
+RUN chown 1001 /work \
+ && chmod "g+rwX" /work \
+ && chown 1001:root /work
+COPY --chown=1001:root build/*-runner /work/application
+
+USER 1001
+
+ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
diff --git a/applications/producer/src/main/kotlin/fr/faraphel/producer/Main.kt b/applications/producer/src/main/kotlin/fr/faraphel/producer/Main.kt
new file mode 100644
index 0000000..cc85006
--- /dev/null
+++ b/applications/producer/src/main/kotlin/fr/faraphel/producer/Main.kt
@@ -0,0 +1,40 @@
+package fr.faraphel.producer
+
+import fr.faraphel.common.error.MissingEnvironmentException
+import fr.faraphel.producer.kafka.TemperatureProducer
+import io.quarkus.runtime.QuarkusApplication
+import io.quarkus.runtime.annotations.QuarkusMain
+
+
+@QuarkusMain
+class Main : QuarkusApplication {
+ override fun run(vararg args: String?): Int {
+ // get the kafka server address
+ val kafkaServer = System.getenv("KAFKA_BOOTSTRAP_SERVERS")
+ ?: throw MissingEnvironmentException("KAFKA_BOOTSTRAP_SERVERS")
+
+ // get the topic name
+ val topicTemperature: String = System.getenv("TEMPERATURE_TOPIC")
+ ?: throw MissingEnvironmentException("TEMPERATURE_TOPIC")
+
+ // get the location of the temperature to get
+ val location = System.getenv("TEMPERATURE_LOCATION")
+ ?: throw MissingEnvironmentException("TEMPERATURE_LOCATION")
+
+ // parse the location to get the latitude and longitude
+ val (latitude, longitude) = location.split(",").map { coordinate -> coordinate.trim().toDouble() }
+
+ // create a producer that will generate temperature values based on the current temperature.
+ val producer = TemperatureProducer(
+ server=kafkaServer,
+ latitude=latitude,
+ longitude=longitude,
+ topic=topicTemperature
+ )
+
+ // indefinitely produce new temperatures values
+ producer.produceForever()
+
+ return 0
+ }
+}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/TemperatureProducer.kt b/applications/producer/src/main/kotlin/fr/faraphel/producer/kafka/TemperatureProducer.kt
similarity index 93%
rename from src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/TemperatureProducer.kt
rename to applications/producer/src/main/kotlin/fr/faraphel/producer/kafka/TemperatureProducer.kt
index 6436119..adb00bc 100644
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/TemperatureProducer.kt
+++ b/applications/producer/src/main/kotlin/fr/faraphel/producer/kafka/TemperatureProducer.kt
@@ -1,8 +1,8 @@
-package fr.faraphel.m1_pe_kafka.kafka
+package fr.faraphel.producer.kafka
import com.google.gson.Gson
import com.google.gson.JsonObject
-import fr.faraphel.m1_pe_kafka.error.http.HttpException
+import fr.faraphel.common.error.http.HttpException
import org.apache.kafka.clients.producer.KafkaProducer
import org.apache.kafka.clients.producer.ProducerConfig
import org.apache.kafka.clients.producer.ProducerRecord
@@ -29,7 +29,7 @@ class TemperatureProducer(
private val latitude: Double,
private val longitude: Double,
private val topic: String,
-) : Thread() {
+) {
companion object {
private val BASE_API_BUILDER: okhttp3.HttpUrl.Builder = okhttp3.HttpUrl.Builder() ///< the Url builder for the API
.scheme("https") // use the https protocol
@@ -78,7 +78,7 @@ class TemperatureProducer(
val response = httpClient.newCall(this.apiRequest).execute()
// check for a successful response
if (!response.isSuccessful)
- // in case of error, raise a http exception
+ // in case of error, raise a http exception
throw HttpException(response.code, response.message)
// parse the response into a map of string to string
@@ -102,13 +102,7 @@ class TemperatureProducer(
// produce a data
this.produce()
// wait for the cooldown
- sleep(frequency)
+ Thread.sleep(frequency)
}
}
-
- /**
- * Thread entrypoint
- * @see produceForever
- */
- override fun run() = this.produceForever()
}
diff --git a/applications/producer/src/main/resources/application.properties b/applications/producer/src/main/resources/application.properties
new file mode 100644
index 0000000..e69de29
diff --git a/docker-compose.yaml b/docker-compose.yaml
index c84c84b..5e147b8 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -27,18 +27,32 @@ services:
depends_on:
- zookeeper
- # Our application
- application:
+ # Our producer
+ producer:
build:
- context: .
+ context: ./applications/producer
+ dockerfile: ./src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
+ environment:
+ KAFKA_BOOTSTRAP_SERVERS: kafka:9092
+ TEMPERATURE_TOPIC: "temperature-celsius"
+ TEMPERATURE_LOCATION: 49.9, 2.3
+ networks:
+ - kafka
+ depends_on:
+ - kafka
+
+ # Our consumer
+ consumer:
+ build:
+ context: ./applications/consumer
dockerfile: ./src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
ports:
- "8080:8080"
environment:
KAFKA_BOOTSTRAP_SERVERS: kafka:9092
- TEMPERATURE_LOCATION: 49.9, 2.3
+ TEMPERATURE_TOPIC: "temperature-fahrenheit"
healthcheck:
- test: curl --fail http://localhost:8080/ping
+ test: curl --fail http://localhost:8080/v1/ping
start_period: 10s
timeout: 5s
interval: 60s
@@ -46,9 +60,24 @@ services:
networks:
- kafka
depends_on:
- - zookeeper
- kafka
+ # Our converter
+ converter:
+ build:
+ context: ./applications/converter
+ dockerfile: ./src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
+ environment:
+ KAFKA_BOOTSTRAP_SERVERS: kafka:9092
+ TEMPERATURE_CELSIUS_TOPIC: "temperature-celsius"
+ TEMPERATURE_FAHRENHEIT_TOPIC: "temperature-fahrenheit"
+ networks:
+ - kafka
+ depends_on:
+ - kafka
+ - producer
+ - consumer
+
networks:
# the Kafka network
kafka:
diff --git a/gradle.properties b/gradle.properties
deleted file mode 100644
index f391fe5..0000000
--- a/gradle.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# Gradle properties
-quarkusPluginId=io.quarkus
-quarkusPluginVersion=3.11.1
-quarkusPlatformGroupId=io.quarkus.platform
-quarkusPlatformArtifactId=quarkus-bom
-quarkusPlatformVersion=3.11.1
-
-# Kafka Properties
-quarkus.analytics.disabled=true
-quarkus.kafka-streams.bootstrap-servers=${KAFKA_BOOTSTRAP_SERVERS:kafka:9092}
-kafka.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS:kafka:9092}
diff --git a/m1-PE-Kafka.iml b/m1-PE-Kafka.iml
new file mode 100644
index 0000000..9a5cfce
--- /dev/null
+++ b/m1-PE-Kafka.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/Main.kt b/src/main/kotlin/fr/faraphel/m1_pe_kafka/Main.kt
deleted file mode 100644
index b1f0dfb..0000000
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/Main.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-package fr.faraphel.m1_pe_kafka
-
-import fr.faraphel.m1_pe_kafka.error.MissingEnvironmentException
-import fr.faraphel.m1_pe_kafka.kafka.AdminUtils
-import fr.faraphel.m1_pe_kafka.kafka.Consumer
-import fr.faraphel.m1_pe_kafka.kafka.Converter
-import fr.faraphel.m1_pe_kafka.kafka.TemperatureProducer
-import fr.faraphel.m1_pe_kafka.rest.TemperatureEndpoint
-import fr.faraphel.m1_pe_kafka.utils.celsius
-import io.quarkus.runtime.Quarkus
-import io.quarkus.runtime.QuarkusApplication
-import io.quarkus.runtime.annotations.QuarkusMain
-import java.time.Instant
-import java.time.ZoneId
-import java.time.format.DateTimeFormatter
-
-
-/**
- * The main class.
- * Contains the entrypoint of the program.
- */
-@QuarkusMain
-class Main : QuarkusApplication {
- /**
- * The entrypoint of the program
- * @param args command line arguments
- * @return the result code of the program
- * @throws MissingEnvironmentException a required environment variable from the configuration is missing
- */
- override fun run(vararg args: String?): Int {
- // create a time formatter
- val timeFormatter = DateTimeFormatter
- .ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
- .withZone(ZoneId.systemDefault())
-
- // get the kafka server address
- val kafkaServer = System.getenv("KAFKA_BOOTSTRAP_SERVERS")
- ?: throw MissingEnvironmentException("KAFKA_BOOTSTRAP_SERVERS")
-
- // get the topics name
- val topicTemperatureCelsius: String = System.getenv("TOPIC_TEMPERATURE_CELSIUS")
- ?: "temperature-celsius"
- val topicTemperatureFahrenheit: String = System.getenv("TOPIC_TEMPERATURE_FAHRENHEIT")
- ?: "temperature-fahrenheit"
-
- // create an admin object
- val admin = AdminUtils(kafkaServer)
- // create our topics
- admin.createTopics(topicTemperatureCelsius, topicTemperatureFahrenheit)
-
- // get the location of the temperature to get
- val location = System.getenv("TEMPERATURE_LOCATION")
- ?: throw MissingEnvironmentException("TEMPERATURE_LOCATION")
-
- // parse the location to get the latitude and longitude
- val (latitude, longitude) = location.split(",").map { coordinate -> coordinate.trim().toDouble() }
-
- // create a producer that will generate temperature values based on the current temperature at Amiens (France).
- val producer = TemperatureProducer(
- server=kafkaServer,
- latitude=latitude,
- longitude=longitude,
- topic=topicTemperatureCelsius
- )
-
- // create a converter that will convert values coming from the Celsius topic to the Fahrenheit topic
- val converter = Converter(
- server=kafkaServer,
- inputTopic=topicTemperatureCelsius,
- outputTopic=topicTemperatureFahrenheit
- ) { temperature -> temperature.celsius.asFahrenheit }
-
- // create a consumer that will print the received values in the Fahrenheit topic
- val consumer = Consumer(
- server=kafkaServer,
- topic=topicTemperatureFahrenheit
- ) { message ->
- // format the time of the message to a proper string
- val time = timeFormatter.format(Instant.ofEpochMilli(message.timestamp()))
- // print the value
- println("[${time}] ${message.value()}°F")
- // update the value for the API
- TemperatureEndpoint.setTemperature(message.value())
- }
-
- // run all the clients
- producer.start()
- consumer.start()
- converter.start()
-
- // wait for them to finish before closing
- producer.join()
- consumer.join()
-
- // close the converter if the others clients are done
- converter.stop()
-
- return 0
- }
-}
-
-/**
- * The main function.
- * Simply call the entrypoint.
- * Used to run the program directly with Kotlin.
- */
-fun main(args: Array) {
- Quarkus.run(Main::class.java, *args)
-}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/AdminUtils.kt b/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/AdminUtils.kt
deleted file mode 100644
index 748013e..0000000
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/kafka/AdminUtils.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package fr.faraphel.m1_pe_kafka.kafka
-
-import org.apache.kafka.clients.admin.Admin
-import org.apache.kafka.clients.admin.AdminClientConfig
-import org.apache.kafka.clients.admin.CreateTopicsResult
-import org.apache.kafka.clients.admin.NewTopic
-import java.util.*
-
-
-/**
- * A wrapper around KafkaAdminClient to simplify its configuration.
- * @param server the kafka server address
- * @param identifier the kafka identifier for the configuration
- * @see Admin
- */
-class AdminUtils(
- private val server: String,
- private val identifier: String = "admin"
-) {
- private val adminConfig = Properties().apply {
- // set the identifier
- this[AdminClientConfig.CLIENT_ID_CONFIG] = identifier
- // set the server information
- this[AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG] = server
- }
-
- private val admin = Admin.create(adminConfig)
-
- /**
- * Create the topics in the kafka server.
- * @param topics the names of the topics to create.
- * @return the result of the operation.
- * @see Admin.createTopics
- */
- fun createTopics(vararg topics: String): CreateTopicsResult {
- // convert the topics name into the corresponding operation
- val operations = topics.map { topic -> NewTopic(topic, 1, 1) }
- // run the command
- return this.admin.createTopics(operations)
- }
-}
diff --git a/src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/TemperatureEndpoint.kt b/src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/TemperatureEndpoint.kt
deleted file mode 100644
index 7472ba1..0000000
--- a/src/main/kotlin/fr/faraphel/m1_pe_kafka/rest/TemperatureEndpoint.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package fr.faraphel.m1_pe_kafka.rest
-
-import jakarta.ws.rs.GET
-import jakarta.ws.rs.Path
-
-
-/**
- * This API endpoint return the latest temperature measured (in Fahrenheit)
- */
-@Path("temperature")
-class TemperatureEndpoint {
- companion object {
- private var temperature: Double? = null ///< the latest temperature value
-
- /**
- * Setter to update the latest temperature value
- * @param temperature the new temperature value
- */
- fun setTemperature(temperature: Double) {
- this.temperature = temperature
- }
- }
-
- /**
- * Handler for a GET request on this endpoint
- * @return the latest temperature measured
- */
- @GET
- fun get(): Double? {
- return temperature
- }
-}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
deleted file mode 100644
index 9817ace..0000000
--- a/src/main/resources/application.properties
+++ /dev/null
@@ -1 +0,0 @@
-quarkus.kafka-streams.application-id=fr.faraphel.m1_pe_kafka