diff --git a/.github/workflows/native-image.yml b/.github/workflows/native-image.yml new file mode 100644 index 0000000..bf27e82 --- /dev/null +++ b/.github/workflows/native-image.yml @@ -0,0 +1,87 @@ +name: Build & Push Native Image + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + check-actor: + runs-on: ubuntu-latest + outputs: + allowed: ${{ steps.check.outputs.allowed }} + steps: + - id: check + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" || "${{ github.actor }}" == "TeamCity" ]]; then + echo "allowed=true" >> "$GITHUB_OUTPUT" + else + echo "allowed=false" >> "$GITHUB_OUTPUT" + fi + + build-and-push: + needs: check-actor + if: needs.check-actor.outputs.allowed == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + strategy: + matrix: + module: + - core + - io + + steps: + - uses: actions/checkout@v4 + + - name: Set up GraalVM + uses: graalvm/setup-graalvm@v1 + with: + java-version: '21' + distribution: 'graalvm-community' + native-image-job-reports: 'true' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: gradle-${{ runner.os }}- + + - name: Build native binary + run: ./gradlew :modules:${{ matrix.module }}:build -Dquarkus.native.enabled=true --no-daemon + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/now-chess/now-chess-systems/${{ matrix.module }} + tags: | + type=sha,prefix=,format=short + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: modules/${{ matrix.module }}/src/main/docker/Dockerfile.native + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=${{ matrix.module }} + cache-to: type=gha,mode=max,scope=${{ matrix.module }} diff --git a/modules/api/src/main/scala/de/nowchess/api/dto/ImportFenRequest.scala b/modules/api/src/main/scala/de/nowchess/api/dto/ImportFenRequest.scala new file mode 100644 index 0000000..9a30b62 --- /dev/null +++ b/modules/api/src/main/scala/de/nowchess/api/dto/ImportFenRequest.scala @@ -0,0 +1,3 @@ +package de.nowchess.api.dto + +case class ImportFenRequest(fen: String) diff --git a/modules/api/src/main/scala/de/nowchess/api/dto/ImportPgnRequest.scala b/modules/api/src/main/scala/de/nowchess/api/dto/ImportPgnRequest.scala new file mode 100644 index 0000000..ef3613a --- /dev/null +++ b/modules/api/src/main/scala/de/nowchess/api/dto/ImportPgnRequest.scala @@ -0,0 +1,3 @@ +package de.nowchess.api.dto + +case class ImportPgnRequest(pgn: String) diff --git a/modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala b/modules/api/src/main/scala/de/nowchess/api/io/GameContextExport.scala similarity index 81% rename from modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala rename to modules/api/src/main/scala/de/nowchess/api/io/GameContextExport.scala index 3968e71..a261742 100644 --- a/modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala +++ b/modules/api/src/main/scala/de/nowchess/api/io/GameContextExport.scala @@ -1,7 +1,6 @@ -package de.nowchess.io +package de.nowchess.api.io import de.nowchess.api.game.GameContext trait GameContextExport: - def exportGameContext(context: GameContext): String diff --git a/modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala b/modules/api/src/main/scala/de/nowchess/api/io/GameContextImport.scala similarity index 83% rename from modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala rename to modules/api/src/main/scala/de/nowchess/api/io/GameContextImport.scala index c56850f..22c149e 100644 --- a/modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala +++ b/modules/api/src/main/scala/de/nowchess/api/io/GameContextImport.scala @@ -1,7 +1,6 @@ -package de.nowchess.io +package de.nowchess.api.io import de.nowchess.api.game.GameContext trait GameContextImport: - def importGameContext(input: String): Either[String, GameContext] diff --git a/modules/core/build.gradle.kts b/modules/core/build.gradle.kts index d572500..d057a24 100644 --- a/modules/core/build.gradle.kts +++ b/modules/core/build.gradle.kts @@ -48,7 +48,6 @@ dependencies { } implementation(project(":modules:api")) - implementation(project(":modules:io")) implementation(project(":modules:rule")) implementation(project(":modules:bot")) @@ -69,6 +68,8 @@ dependencies { implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}") + testImplementation(project(":modules:io")) + testImplementation(platform("org.junit:junit-bom:5.13.4")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}") diff --git a/modules/core/src/main/docker/Dockerfile.native b/modules/core/src/main/docker/Dockerfile.native index 57defbf..c5e5c1d 100644 --- a/modules/core/src/main/docker/Dockerfile.native +++ b/modules/core/src/main/docker/Dockerfile.native @@ -3,7 +3,7 @@ # # Before building the container image run: # -# ./gradlew build -Dquarkus.native.enabled=true +# ./gradlew :modules:core:build -Dquarkus.native.enabled=true # # Then, build the image with: # @@ -13,7 +13,7 @@ # # docker run -i --rm -p 8080:8080 quarkus/backcore # -# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9. +# The `registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9. # To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. ### FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7 @@ -21,7 +21,7 @@ WORKDIR /work/ RUN chown 1001 /work \ && chmod "g+rwX" /work \ && chown 1001:root /work -COPY --chown=1001:root --chmod=0755 build/*-runner /work/application +COPY --chown=1001:root --chmod=0755 modules/core/build/*-runner /work/application EXPOSE 8080 USER 1001 diff --git a/modules/core/src/main/scala/de/nowchess/chess/client/IoServiceClient.scala b/modules/core/src/main/scala/de/nowchess/chess/client/IoServiceClient.scala index dac4c3d..bc41b1b 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/client/IoServiceClient.scala +++ b/modules/core/src/main/scala/de/nowchess/chess/client/IoServiceClient.scala @@ -1,7 +1,7 @@ package de.nowchess.chess.client +import de.nowchess.api.dto.{ImportFenRequest, ImportPgnRequest} import de.nowchess.api.game.GameContext -import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest} import jakarta.ws.rs.* import jakarta.ws.rs.core.MediaType import org.eclipse.microprofile.rest.client.inject.RegisterRestClient diff --git a/modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala b/modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala index 816cb0b..1393880 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala +++ b/modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.module.SimpleModule import com.fasterxml.jackson.module.scala.DefaultScalaModule import de.nowchess.api.board.Square -import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer} import io.quarkus.jackson.ObjectMapperCustomizer import jakarta.inject.Singleton diff --git a/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeyDeserializer.scala b/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeyDeserializer.scala new file mode 100644 index 0000000..35e9604 --- /dev/null +++ b/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeyDeserializer.scala @@ -0,0 +1,8 @@ +package de.nowchess.chess.config + +import com.fasterxml.jackson.databind.{DeserializationContext, KeyDeserializer} +import de.nowchess.api.board.Square + +class SquareKeyDeserializer extends KeyDeserializer: + override def deserializeKey(key: String, ctx: DeserializationContext): AnyRef = + Square.fromAlgebraic(key).orNull diff --git a/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeySerializer.scala b/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeySerializer.scala new file mode 100644 index 0000000..c0ca0d0 --- /dev/null +++ b/modules/core/src/main/scala/de/nowchess/chess/config/SquareKeySerializer.scala @@ -0,0 +1,9 @@ +package de.nowchess.chess.config + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.{JsonSerializer, SerializerProvider} +import de.nowchess.api.board.Square + +class SquareKeySerializer extends JsonSerializer[Square]: + override def serialize(value: Square, gen: JsonGenerator, provider: SerializerProvider): Unit = + gen.writeFieldName(value.toString) diff --git a/modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala b/modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala index 43f099b..0759048 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala +++ b/modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala @@ -7,7 +7,7 @@ import de.nowchess.api.player.{PlayerId, PlayerInfo} import de.nowchess.chess.controller.Parser import de.nowchess.chess.observer.* import de.nowchess.chess.command.{CommandInvoker, MoveCommand, MoveResult} -import de.nowchess.io.{GameContextExport, GameContextImport} +import de.nowchess.api.io.{GameContextExport, GameContextImport} import de.nowchess.rules.RuleSet import de.nowchess.rules.sets.DefaultRules diff --git a/modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala b/modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala index ece9752..e5dbf94 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala +++ b/modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala @@ -12,9 +12,6 @@ import de.nowchess.chess.engine.GameEngine import de.nowchess.chess.exception.{BadRequestException, GameNotFoundException} import de.nowchess.chess.observer.* import de.nowchess.chess.registry.{GameEntry, GameRegistry} -import de.nowchess.io.fen.FenExporter -import de.nowchess.io.pgn.PgnExporter -import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest} import io.smallrye.mutiny.Multi import jakarta.enterprise.context.ApplicationScoped import jakarta.inject.Inject @@ -89,16 +86,8 @@ class GameResource: private def toGameStateDto(entry: GameEntry): GameStateDto = val ctx = entry.engine.context GameStateDto( - fen = FenExporter.exportGameContext(ctx), - pgn = PgnExporter.exportGame( - Map( - "Event" -> "NowChess game", - "White" -> entry.white.displayName, - "Black" -> entry.black.displayName, - "Result" -> "*", - ), - ctx.moves, - ), + fen = ioClient.exportFen(ctx), + pgn = ioClient.exportPgn(ctx), turn = ctx.turn.label.toLowerCase, status = statusOf(entry), winner = ctx.result.collect { case GameResult.Win(c) => c.label.toLowerCase }, diff --git a/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala b/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala index 371f2dd..e07336b 100644 --- a/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala +++ b/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala @@ -4,7 +4,7 @@ import de.nowchess.api.board.{Board, Color, File, PieceType, Rank, Square} import de.nowchess.api.game.GameContext import de.nowchess.api.move.{Move, MoveType, PromotionPiece} import de.nowchess.chess.observer.{GameEvent, InvalidMoveEvent, InvalidMoveReason, MoveRedoneEvent, Observer} -import de.nowchess.io.GameContextImport +import de.nowchess.api.io.GameContextImport import de.nowchess.rules.RuleSet import de.nowchess.rules.sets.DefaultRules import org.scalatest.funsuite.AnyFunSuite diff --git a/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala b/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala index d926147..91ac2b1 100644 --- a/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala +++ b/modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala @@ -1,16 +1,13 @@ package de.nowchess.chess.engine -import scala.collection.mutable -import de.nowchess.api.board.{Board, Color} -import de.nowchess.api.game.GameContext -import de.nowchess.chess.observer.{GameEvent, Observer, PgnLoadedEvent} -import de.nowchess.io.pgn.PgnParser +import de.nowchess.chess.observer.{GameEvent, Observer} import de.nowchess.io.fen.FenParser -import de.nowchess.io.pgn.PgnExporter -import de.nowchess.io.fen.FenExporter +import de.nowchess.io.pgn.{PgnExporter, PgnParser} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import scala.collection.mutable + class GameEngineLoadGameTest extends AnyFunSuite with Matchers: test("loadGame with PgnParser: loads valid PGN and enables undo/redo"): diff --git a/modules/io/src/main/docker/Dockerfile.jvm b/modules/io/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..c3c09fc --- /dev/null +++ b/modules/io/src/main/docker/Dockerfile.jvm @@ -0,0 +1,100 @@ +#### +# 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/backcore-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/backcore-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/backcore-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") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - 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") +# +# You can find more information about the UBI base runtime images and their configuration here: +# https://rh-openjdk.github.io/redhat-openjdk-containers/ +### +FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.24 + +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/ + +EXPOSE 8080 +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/modules/io/src/main/docker/Dockerfile.legacy-jar b/modules/io/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..8c89666 --- /dev/null +++ b/modules/io/src/main/docker/Dockerfile.legacy-jar @@ -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 -Dquarkus.package.jar.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/backcore-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/backcore-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/backcore-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") - Be aware that this will override +# the default JVM options, use `JAVA_OPTS_APPEND` to append options +# - 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") +# +# You can find more information about the UBI base runtime images and their configuration here: +# https://rh-openjdk.github.io/redhat-openjdk-containers/ +### +FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.24 + +ENV LANGUAGE='en_US:en' + + +COPY build/lib/* /deployments/lib/ +COPY build/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +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/modules/io/src/main/docker/Dockerfile.native b/modules/io/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..a6d6afd --- /dev/null +++ b/modules/io/src/main/docker/Dockerfile.native @@ -0,0 +1,29 @@ +#### +# 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 :modules:io:build -Dquarkus.native.enabled=true +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/backio . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/backio +# +# The `registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`. +### +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 modules/io/build/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/modules/io/src/main/docker/Dockerfile.native-micro b/modules/io/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..9408243 --- /dev/null +++ b/modules/io/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,32 @@ +#### +# 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/backcore . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/backcore +# +# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9. +# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`. +### +FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root --chmod=0755 build/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/modules/io/src/main/scala/de/nowchess/io/GameFileService.scala b/modules/io/src/main/scala/de/nowchess/io/GameFileService.scala index f84d4fb..9bbfc44 100644 --- a/modules/io/src/main/scala/de/nowchess/io/GameFileService.scala +++ b/modules/io/src/main/scala/de/nowchess/io/GameFileService.scala @@ -1,6 +1,8 @@ package de.nowchess.io import de.nowchess.api.game.GameContext +import de.nowchess.api.io.{GameContextExport, GameContextImport} + import java.nio.file.{Files, Path} import java.nio.charset.StandardCharsets import scala.util.Try diff --git a/modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala b/modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala index 8ba9303..377459b 100644 --- a/modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala +++ b/modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala @@ -2,7 +2,7 @@ package de.nowchess.io.fen import de.nowchess.api.board.* import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextExport +import de.nowchess.api.io.GameContextExport object FenExporter extends GameContextExport: diff --git a/modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala b/modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala index 1f206ff..179ae95 100644 --- a/modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala +++ b/modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala @@ -2,7 +2,7 @@ package de.nowchess.io.fen import de.nowchess.api.board.* import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextImport +import de.nowchess.api.io.GameContextImport object FenParser extends GameContextImport: diff --git a/modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala b/modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala index f51b46f..11c4416 100644 --- a/modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala +++ b/modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala @@ -2,9 +2,10 @@ package de.nowchess.io.fen import de.nowchess.api.board.* import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextImport + import scala.util.parsing.combinator.RegexParsers import FenParserSupport.* +import de.nowchess.api.io.GameContextImport object FenParserCombinators extends RegexParsers with GameContextImport: diff --git a/modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala b/modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala index 4a8ffca..5ed7aec 100644 --- a/modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala +++ b/modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala @@ -4,8 +4,8 @@ import fastparse.* import fastparse.NoWhitespace.* import de.nowchess.api.board.* import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextImport import FenParserSupport.* +import de.nowchess.api.io.GameContextImport object FenParserFastParse extends GameContextImport: diff --git a/modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala b/modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala index 44ba4cc..e33be19 100644 --- a/modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala +++ b/modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala @@ -6,8 +6,9 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule import de.nowchess.api.board.* import de.nowchess.api.move.{Move, MoveType, PromotionPiece} import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextExport +import de.nowchess.api.io.GameContextExport import de.nowchess.io.pgn.PgnExporter + import java.time.{LocalDate, ZoneId, ZonedDateTime} /** Exports a GameContext to a comprehensive JSON format using Jackson. diff --git a/modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala b/modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala index ca283a6..1827434 100644 --- a/modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala +++ b/modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala @@ -5,7 +5,8 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule import de.nowchess.api.board.* import de.nowchess.api.move.{Move, MoveType, PromotionPiece} import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextImport +import de.nowchess.api.io.GameContextImport + import scala.util.Try /** Imports a GameContext from JSON format using Jackson. diff --git a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala index afb697a..4a7b832 100644 --- a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala +++ b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala @@ -3,7 +3,7 @@ package de.nowchess.io.pgn import de.nowchess.api.board.* import de.nowchess.api.move.{Move, MoveType, PromotionPiece} import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextExport +import de.nowchess.api.io.GameContextExport import de.nowchess.rules.sets.DefaultRules object PgnExporter extends GameContextExport: diff --git a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala index 561e2da..27a81cd 100644 --- a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala +++ b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala @@ -3,7 +3,7 @@ package de.nowchess.io.pgn import de.nowchess.api.board.* import de.nowchess.api.move.{Move, MoveType, PromotionPiece} import de.nowchess.api.game.GameContext -import de.nowchess.io.GameContextImport +import de.nowchess.api.io.GameContextImport import de.nowchess.rules.sets.DefaultRules /** A parsed PGN game containing headers and the resolved move list. */ diff --git a/modules/io/src/test/scala/de/nowchess/io/GameFileServiceSuite.scala b/modules/io/src/test/scala/de/nowchess/io/GameFileServiceSuite.scala index 62d2f40..32056ca 100644 --- a/modules/io/src/test/scala/de/nowchess/io/GameFileServiceSuite.scala +++ b/modules/io/src/test/scala/de/nowchess/io/GameFileServiceSuite.scala @@ -1,13 +1,14 @@ package de.nowchess.io -import de.nowchess.api.game.GameContext import de.nowchess.api.board.{File, Rank, Square} +import de.nowchess.api.game.GameContext +import de.nowchess.api.io.GameContextExport import de.nowchess.api.move.Move import de.nowchess.io.json.{JsonExporter, JsonParser} -import java.nio.file.{Files, Paths} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers -import scala.util.Using + +import java.nio.file.{Files, Paths} class GameFileServiceSuite extends AnyFunSuite with Matchers: