diff --git a/EpcQrCodeRest/.dockerignore b/EpcQrCodeRest/.dockerignore new file mode 100644 index 0000000..4361d2f --- /dev/null +++ b/EpcQrCodeRest/.dockerignore @@ -0,0 +1,5 @@ +* +!build/*-runner +!build/*-runner.jar +!build/lib/* +!build/quarkus-app/* \ No newline at end of file diff --git a/EpcQrCodeRest/build.gradle b/EpcQrCodeRest/build.gradle new file mode 100644 index 0000000..7c83017 --- /dev/null +++ b/EpcQrCodeRest/build.gradle @@ -0,0 +1,64 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id "org.jetbrains.kotlin.plugin.allopen" version "1.4.10" + id 'io.quarkus' +} + +group 'net.dankito.banking.epcqrcode' +version '1.0.0-SNAPSHOT' + + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +compileKotlin { + kotlinOptions.jvmTarget = JavaVersion.VERSION_11 + kotlinOptions.javaParameters = true +} + +compileTestKotlin { + kotlinOptions.jvmTarget = JavaVersion.VERSION_11 +} + + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation enforcedPlatform("io.quarkus:quarkus-universe-bom:$quarkusVersion") + implementation 'io.quarkus:quarkus-kotlin' + implementation 'io.quarkus:quarkus-resteasy' + implementation 'io.quarkus:quarkus-resteasy-jackson' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + + implementation 'io.quarkus:quarkus-arc' + + implementation project(":EpcQrCode") + + + testImplementation 'io.quarkus:quarkus-junit5' + testImplementation 'io.rest-assured:rest-assured' +} + + +quarkus { + setOutputDirectory("$projectDir/build/classes/kotlin/main") +} + +quarkusDev { + setSourceDir("$projectDir/src/main/kotlin") +} + +allOpen { + annotation("javax.ws.rs.Path") + annotation("javax.enterprise.context.ApplicationScoped") + annotation("io.quarkus.test.junit.QuarkusTest") +} + +test { + useJUnitPlatform() +} diff --git a/EpcQrCodeRest/src/main/docker/Dockerfile.fast-jar b/EpcQrCodeRest/src/main/docker/Dockerfile.fast-jar new file mode 100644 index 0000000..ea46942 --- /dev/null +++ b/EpcQrCodeRest/src/main/docker/Dockerfile.fast-jar @@ -0,0 +1,57 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# mvn package -Dquarkus.package.type=fast-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.fast-jar -t quarkus/epc-qr-code-rest-fast-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/epc-qr-code-rest-fast-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/epc-qr-code-rest-fast-jar +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 + +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' + +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=1001 build/quarkus-app/lib/ /deployments/lib/ +COPY --chown=1001 build/quarkus-app/*.jar /deployments/ +COPY --chown=1001 build/quarkus-app/app/ /deployments/app/ +COPY --chown=1001 build/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] diff --git a/EpcQrCodeRest/src/main/docker/Dockerfile.jvm b/EpcQrCodeRest/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..af63015 --- /dev/null +++ b/EpcQrCodeRest/src/main/docker/Dockerfile.jvm @@ -0,0 +1,54 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# mvn package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/epc-qr-code-rest-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/epc-qr-code-rest-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005) like this : EXPOSE 8080 5050 +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 -p 5005:5005 -e JAVA_ENABLE_DEBUG="true" quarkus/epc-qr-code-rest-jvm +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 + +ARG JAVA_PACKAGE=java-11-openjdk-headless +ARG RUN_JAVA_VERSION=1.3.8 + +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' + +# Install java and the run-java script +# Also set up permissions for user `1001` +RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ + && microdnf update \ + && microdnf clean all \ + && mkdir /deployments \ + && chown 1001 /deployments \ + && chmod "g+rwX" /deployments \ + && chown 1001:root /deployments \ + && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ + && chown 1001 /deployments/run-java.sh \ + && chmod 540 /deployments/run-java.sh \ + && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security + +# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size. +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" + +COPY build/lib/* /deployments/lib/ +COPY build/*-runner.jar /deployments/app.jar + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/EpcQrCodeRest/src/main/docker/Dockerfile.native b/EpcQrCodeRest/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..cf8a20d --- /dev/null +++ b/EpcQrCodeRest/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the container image run: +# +# mvn package -Pnative -Dquarkus.native.container-build=true +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/epc-qr-code-rest . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/epc-qr-code-rest +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root build/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResource.kt b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResource.kt new file mode 100644 index 0000000..388b7e0 --- /dev/null +++ b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResource.kt @@ -0,0 +1,69 @@ +package net.codinux.banking.epcqrcode.rest + +import net.codinux.banking.epcqrcode.CreatorParam +import net.codinux.banking.epcqrcode.EncodeToQrCodeConfig +import net.codinux.banking.epcqrcode.EpcQrCodeCreator +import net.codinux.banking.epcqrcode.QrCodeGenerator +import net.codinux.banking.epcqrcode.rest.dto.GenerateEpcQrCodeRequestDto +import net.codinux.banking.epcqrcode.rest.dto.GenerateEpcQrCodeResponseDto +import org.slf4j.LoggerFactory +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.Produces +import javax.ws.rs.core.MediaType +import javax.ws.rs.core.Response + + +@Path("/qrcode") +class EpcQrCodeResource { + + companion object { + private val log = LoggerFactory.getLogger(EpcQrCodeResource::class.java) + } + + + protected val epcQrCodeCreator = EpcQrCodeCreator() + + protected val qrCodeGenerator = QrCodeGenerator() + + + @POST + @Produces(MediaType.APPLICATION_JSON) + fun createEpcQrCode(requestDto: GenerateEpcQrCodeRequestDto): Response { + try { + val qrCodeContent = epcQrCodeCreator.generateAsString(mapToCreatorParam(requestDto)) + val qrCodeBytes = qrCodeGenerator.generateQrCode(qrCodeContent, map(requestDto)) + + return Response.ok(GenerateEpcQrCodeResponseDto(qrCodeBytes)) + .header("Access-Control-Allow-Origin", "*") + .build() + } catch (e: Exception) { + log.error("Could not create QR code for $requestDto", e) + + return Response.serverError() + .header("Access-Control-Allow-Origin", "*") + .build() + } + } + + private fun mapToCreatorParam(dto: GenerateEpcQrCodeRequestDto): CreatorParam { + return CreatorParam( + dto.receiverName, + dto.iban, + dto.bic, + dto.amount, + dto.reference, + dto.noteToUser + ) + } + + private fun map(dto: GenerateEpcQrCodeRequestDto): EncodeToQrCodeConfig { + return EncodeToQrCodeConfig( + dto.imageWidth, + dto.imageHeight, + dto.imageFormat, + dto.encoding + ) + } + +} \ No newline at end of file diff --git a/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeRequestDto.kt b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeRequestDto.kt new file mode 100644 index 0000000..fb73bdd --- /dev/null +++ b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeRequestDto.kt @@ -0,0 +1,29 @@ +package net.codinux.banking.epcqrcode.rest.dto + +import net.codinux.banking.epcqrcode.EpcQrCodeCharacterSet +import net.codinux.banking.epcqrcode.ImageFormat + + +class GenerateEpcQrCodeRequestDto { + + var receiverName: String = "" + + var bic: String? = null + + var iban: String = "" + + var amount: Double? = null + + var reference: String? = null + + var noteToUser: String? = null + + var imageWidth: Int = 500 + + var imageHeight: Int = 500 + + var imageFormat: ImageFormat = ImageFormat.PNG + + var encoding: EpcQrCodeCharacterSet = EpcQrCodeCharacterSet.UTF_8 + +} \ No newline at end of file diff --git a/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeResponseDto.kt b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeResponseDto.kt new file mode 100644 index 0000000..93e9eba --- /dev/null +++ b/EpcQrCodeRest/src/main/kotlin/net/codinux/banking/epcqrcode/rest/dto/GenerateEpcQrCodeResponseDto.kt @@ -0,0 +1,7 @@ +package net.codinux.banking.epcqrcode.rest.dto + + +class GenerateEpcQrCodeResponseDto( + // JAX-RS automatically encodes ByteArray to Base64 + val qrCodeBase64Encoded: ByteArray +) \ No newline at end of file diff --git a/EpcQrCodeRest/src/main/resources/application.properties b/EpcQrCodeRest/src/main/resources/application.properties new file mode 100644 index 0000000..2c47177 --- /dev/null +++ b/EpcQrCodeRest/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.http.port=6543 + +quarkus.native.enable-https-url-handler=true +quarkus.native.enable-all-security-services=true \ No newline at end of file diff --git a/EpcQrCodeRest/src/native-test/kotlin/net/codinux/banking/epcqrcode/rest/NativeEpcQrCodeResourceIT.kt b/EpcQrCodeRest/src/native-test/kotlin/net/codinux/banking/epcqrcode/rest/NativeEpcQrCodeResourceIT.kt new file mode 100644 index 0000000..2ce0b3b --- /dev/null +++ b/EpcQrCodeRest/src/native-test/kotlin/net/codinux/banking/epcqrcode/rest/NativeEpcQrCodeResourceIT.kt @@ -0,0 +1,7 @@ +package net.codinux.banking.epcqrcode.rest + +import io.quarkus.test.junit.NativeImageTest + + +@NativeImageTest +class NativeEpcQrCodeResourceIT : EpcQrCodeResourceTest() \ No newline at end of file diff --git a/EpcQrCodeRest/src/test/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResourceTest.kt b/EpcQrCodeRest/src/test/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResourceTest.kt new file mode 100644 index 0000000..db42910 --- /dev/null +++ b/EpcQrCodeRest/src/test/kotlin/net/codinux/banking/epcqrcode/rest/EpcQrCodeResourceTest.kt @@ -0,0 +1,24 @@ +package net.codinux.banking.epcqrcode.rest + +import io.quarkus.test.junit.QuarkusTest +import io.restassured.RestAssured +import net.codinux.banking.epcqrcode.rest.dto.GenerateEpcQrCodeRequestDto +import org.hamcrest.CoreMatchers +import org.junit.jupiter.api.Test + + +@QuarkusTest +class EpcQrCodeResourceTest { + + @Test + fun createEpcQrCode() { + RestAssured.given() + .header("content-type", "application/json") + .body(GenerateEpcQrCodeRequestDto()) + .`when`().post("/qrcode") + .then() + .statusCode(200) + .body(CoreMatchers.containsString("qrCodeBase64Encoded")) + } + +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d10aee5..30c8140 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,6 @@ kotlin.code.style=official kotlin.js.generate.executable.default=false xcodeproj=./EpcQrCodeiOSApp + + +quarkusVersion=1.8.2.Final diff --git a/settings.gradle.kts b/settings.gradle.kts index 63ffd2d..c1ae01e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,10 @@ pluginManagement { } } } + + plugins { + id("io.quarkus") version "1.8.2.Final" // TODO: why doesn't he find quarkusVersion? + } } @@ -21,4 +25,4 @@ rootProject.name = "EpcQrCode" include(":EpcQrCode") include(":EpcQrCodeAndroidApp") include(":EpcQrCodeJavaFxApp") - +include(":EpcQrCodeRest")