<?xml version="1.0" encoding="UTF-8"?>
<!--

     Copyright 2017-2026 Adobe.

     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

             http://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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.adobe.testing</groupId>
    <artifactId>s3mock-parent</artifactId>
    <version>5.2.2</version>
  </parent>

  <artifactId>s3mock</artifactId>
  <packaging>jar</packaging>

  <name>S3Mock - Server</name>

  <properties>
    <start-class>com.adobe.testing.s3mock.S3MockApplication</start-class>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-reflect</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-stdlib</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
    <!-- Provides the Java implementations of CRC32/CRC32C/SHA checksums. CRC64NVME has no Java
         fallback here (it requires the native aws-crt library), so S3Mock supplies its own
         pure-JVM Crc64Nvme implementation instead of pulling in the ~20MB aws-crt dependency. -->
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>checksums</artifactId>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>utils</artifactId>
    </dependency>
    <dependency>
      <groupId>tools.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
      <groupId>tools.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>
    <dependency>
      <groupId>tools.jackson.module</groupId>
      <artifactId>jackson-module-kotlin</artifactId>
    </dependency>
    <dependency>
      <groupId>com.code-intelligence</groupId>
      <artifactId>jazzer-junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.tngtech.archunit</groupId>
      <artifactId>archunit-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>digital.pragmatech.testing</groupId>
      <artifactId>spring-test-profiler</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.glassfish.jaxb</groupId>
      <artifactId>jaxb-runtime</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.jetbrains.kotlin</groupId>
      <artifactId>kotlin-test-junit</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito.kotlin</groupId>
      <artifactId>mockito-kotlin</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-restclient</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-webmvc-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.xmlunit</groupId>
      <artifactId>xmlunit-assertj3</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>auth</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Test-only: the AWS SDK chunk-encoder used to build CRC64NVME test fixtures requires the
         native aws-crt library. Not needed at runtime - S3Mock computes CRC64NVME via its own
         pure-JVM Crc64Nvme - so this stays test-scoped and out of the packaged fat jar. -->
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>aws-crt-client</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-checkstyle-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <mainClass>${start-class}</mainClass>
          <classifier>exec</classifier>
          <!--
            Shared OCI image build configuration. Maven merges this plugin-level
            configuration into the build-image executions declared in the
            build-docker-image and push-docker-image profiles, so the common
            Buildpacks env only needs to be defined once here.
          -->
          <image>
            <!--
              Use BellSoft's Alpaquita Linux (musl) Cloud Native Buildpacks builder instead of
              Spring Boot's default Paketo noble-java-tiny builder. The musl builder produces a
              smaller image and defaults to the bellsoft/buildpacks.hardened-run:musl run image
              (immutable, proactively CVE-patched, SBOM included), which reduces the number of
              CVEs reported by users' image scanners. Multi-arch (linux/amd64 + linux/arm64).
            -->
            <builder>bellsoft/buildpacks.builder:musl</builder>
            <!--
              Use the locally built custom run image (server/src/main/docker/run-image/Dockerfile)
              which pre-creates a `cnb`-owned /s3mockroot so a mounted named volume is writable by
              the non-root user (see https://github.com/adobe/S3Mock/issues/3139). The concrete
              runImage tag is set per build-image execution below (it is architecture specific).
              IF_NOT_PRESENT keeps the buildpack from trying to pull our local-only run image from a
              registry, while still pulling the builder on a clean machine when it is absent.
            -->
            <pullPolicy>IF_NOT_PRESENT</pullPolicy>
            <env>
              <BP_JVM_VERSION>${java.version}</BP_JVM_VERSION>
              <!-- Run jdeps + jlink to produce a minimal custom JRE (only required modules). -->
              <BP_JVM_JLINK_ENABLED>true</BP_JVM_JLINK_ENABLED>
              <!--
                Tighten the jlink output beyond the buildpack default: keep only the server VM
                (drops the unused client + minimal libjvm.so variants, ~15MB), and use the JDK 21+
                zip-9 module compression (shrinks lib/modules by ~10MB). musl only trims the OS
                layer, not the JRE, so these flags are what actually reduce the JRE layer. All GCs
                (including Serial GC) and C2 live in the server VM, so runtime behaviour is unchanged.
              -->
              <BP_JVM_JLINK_ARGS>--no-man-pages --no-header-files --strip-debug --compress=zip-9 --vm=server</BP_JVM_JLINK_ARGS>
              <!-- S3Mock does not use Spring Cloud Bindings; disable to remove the ~94MB layer. -->
              <BP_SPRING_CLOUD_BINDINGS_DISABLED>true</BP_SPRING_CLOUD_BINDINGS_DISABLED>
              <!--
                Memory-footprint tuning for the launched container. BPL_JVM_THREAD_COUNT is only
                the estimate the Paketo memory calculator uses to reserve thread-stack space
                (threads x -Xss); lowering it from the 250 default frees memory for the heap under
                a container memory limit (it does not cap the actual number of threads).
                It must use the OVERRIDE type: a DEFAULT-typed launch env is not visible to the
                memory-calculator exec.d process, so it would silently have no effect. The
                trade-off is that a runtime `-e BPL_JVM_THREAD_COUNT=...` is ignored.
              -->
              <BPE_OVERRIDE_BPL_JVM_THREAD_COUNT>50</BPE_OVERRIDE_BPL_JVM_THREAD_COUNT>
              <!--
                Footprint tuning: a smaller reserved code cache (vs the 240M default) and a 512k thread stack.
                The memory calculator honors these user-provided values instead of overriding them.
                Serial GC (not ZGC) because at this heap size (~108M under the 256Mi container cap)
                ZGC's concurrent-collection bookkeeping and worker threads cost more than they save;
                Serial's single-threaded, fully-compacting collections reclaim memory more reliably
                under the concurrent load in ConcurrencyIT, and S3Mock has no pause-time SLA to justify ZGC.
              -->
              <BPE_APPEND_JAVA_TOOL_OPTIONS>-XX:+UseSerialGC -Djava.security.egd=file:/dev/./urandom -XX:ReservedCodeCacheSize=32M -Xss512k</BPE_APPEND_JAVA_TOOL_OPTIONS>
              <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS>
              <BPE_DEFAULT_LANG>en_US.UTF-8</BPE_DEFAULT_LANG>
              <BPE_DEFAULT_LANGUAGE>en_US:en</BPE_DEFAULT_LANGUAGE>
              <BPE_DEFAULT_LC_ALL>en_US.UTF-8</BPE_DEFAULT_LC_ALL>
            </env>
          </image>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <!--
      Build a local, single-architecture OCI image via Cloud Native Buildpacks using the
      BellSoft Alpaquita Linux (musl) builder configured on the spring-boot-maven-plugin above.
      No imagePlatform is set, so Docker pulls the builder/run images matching the host
      architecture and the produced image matches the local Docker daemon automatically.
      Active by default; skipped with -DskipDocker. The integration-tests and
      testcontainers modules consume this image for local testing.
    -->
    <profile>
      <id>build-docker-image</id>
      <activation>
        <property>
          <!-- If running with -DskipDocker, this profile won't be active. -->
          <name>!skipDocker</name>
        </property>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <executions>
              <execution>
                <id>parse-version</id>
                <goals>
                  <goal>parse-version</goal>
                </goals>
                <phase>validate</phase>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <executions>
              <!--
                Build the custom run image (host architecture) before build-image runs, so the
                buildpack can resolve it locally via the IF_NOT_PRESENT pull policy. It pre-creates
                a `cnb`-owned /s3mockroot so named volumes are writable by the non-root user.
              -->
              <execution>
                <id>build-run-image</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>prepare-package</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>build</argument>
                    <argument>--tag</argument>
                    <argument>${docker.run.image.name}:musl</argument>
                    <argument>${project.basedir}/src/main/docker/run-image</argument>
                  </arguments>
                </configuration>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
              <execution>
                <id>build-image</id>
                <goals>
                  <goal>build-image</goal>
                </goals>
                <phase>package</phase>
                <configuration>
                  <image>
                    <name>${docker.image.name}:${project.version}</name>
                    <runImage>${docker.run.image.name}:musl</runImage>
                    <!-- Version tag names: major, minor, latest (patch is the image name above). -->
                    <tags>
                      <tag>${docker.image.name}:${parsedVersion.majorVersion}</tag>
                      <tag>${docker.image.name}:${parsedVersion.majorVersion}.${parsedVersion.minorVersion}</tag>
                      <tag>${docker.image.name}:latest</tag>
                    </tags>
                  </image>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <!--
      Release-only profile: builds both linux/amd64 and linux/arm64 images (arm64 via QEMU
      emulation on an amd64 runner), publishes each to a temporary per-architecture tag on
      Docker Hub, then merges them into a single multi-architecture manifest for the final tags.

      Duplicates the build-helper-maven-plugin/exec-maven-plugin declarations from
      build-docker-image above rather than sharing them: the two profiles are mutually
      exclusive at release time (the root pom's release goals pass
      "-P!build-docker-image -Ppush-docker-image"), and this profile's executions build/push
      both architectures instead of just the host one.
    -->
    <profile>
      <id>push-docker-image</id>
      <activation>
        <activeByDefault>false</activeByDefault>
      </activation>
      <build>
        <plugins>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <executions>
              <execution>
                <id>parse-version</id>
                <goals>
                  <goal>parse-version</goal>
                </goals>
                <phase>validate</phase>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <executions>
              <!--
                Pre-pull the builder for each architecture into a distinct local tag before
                build-image runs. The plugin-wide pullPolicy is IF_NOT_PRESENT (needed so the
                locally-built, registry-less run image below resolves without a network pull),
                but that policy is shared with the builder image: since both architectures would
                otherwise reference the same `bellsoft/buildpacks.builder:musl` tag, whichever
                architecture's build-image execution runs first leaves that tag cached, and the
                second execution's IF_NOT_PRESENT check finds it "present" and reuses the wrong
                platform instead of pulling its own (https://github.com/adobe/S3Mock/issues/3139).
                Tagging each pulled platform under its own local tag, mirroring the run-image
                pattern below, keeps both platform variants available simultaneously so the
                matching build-image execution can reference the correct one directly.
              -->
              <execution>
                <id>pull-builder-amd64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>pull</argument>
                    <argument>--platform</argument>
                    <argument>linux/amd64</argument>
                    <argument>bellsoft/buildpacks.builder:musl</argument>
                  </arguments>
                </configuration>
              </execution>
              <execution>
                <id>tag-builder-amd64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>tag</argument>
                    <argument>bellsoft/buildpacks.builder:musl</argument>
                    <argument>bellsoft/buildpacks.builder:musl-amd64</argument>
                  </arguments>
                </configuration>
              </execution>
              <execution>
                <id>pull-builder-arm64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>pull</argument>
                    <argument>--platform</argument>
                    <argument>linux/arm64</argument>
                    <argument>bellsoft/buildpacks.builder:musl</argument>
                  </arguments>
                </configuration>
              </execution>
              <execution>
                <id>tag-builder-arm64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>initialize</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>tag</argument>
                    <argument>bellsoft/buildpacks.builder:musl</argument>
                    <argument>bellsoft/buildpacks.builder:musl-arm64</argument>
                  </arguments>
                </configuration>
              </execution>
              <!--
                Build the custom run image for each architecture before build-image runs, so the
                buildpack resolves it locally (IF_NOT_PRESENT) instead of pulling from a registry.
                A local Docker image tag holds a single architecture, so amd64 and arm64 get
                distinct tags referenced by the matching build-image execution below. arm64 is
                emulated via QEMU (buildx) on the amd64 release runner. The run image only adds a
                `cnb`-owned /s3mockroot so mounted named volumes are writable by the non-root user.
              -->
              <execution>
                <id>build-run-image-amd64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>prepare-package</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>buildx</argument>
                    <argument>build</argument>
                    <argument>--platform</argument>
                    <argument>linux/amd64</argument>
                    <argument>--load</argument>
                    <argument>--tag</argument>
                    <argument>${docker.run.image.name}:musl-amd64</argument>
                    <argument>${project.basedir}/src/main/docker/run-image</argument>
                  </arguments>
                </configuration>
              </execution>
              <execution>
                <id>build-run-image-arm64</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>prepare-package</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>buildx</argument>
                    <argument>build</argument>
                    <argument>--platform</argument>
                    <argument>linux/arm64</argument>
                    <argument>--load</argument>
                    <argument>--tag</argument>
                    <argument>${docker.run.image.name}:musl-arm64</argument>
                    <argument>${project.basedir}/src/main/docker/run-image</argument>
                  </arguments>
                </configuration>
              </execution>
              <!--
                Merge the two per-architecture images into a single multi-arch manifest for the
                final tags. Runs after both images have been published (package phase).
              -->
              <execution>
                <id>merge-multi-arch-manifest</id>
                <goals>
                  <goal>exec</goal>
                </goals>
                <phase>install</phase>
                <configuration>
                  <executable>docker</executable>
                  <arguments>
                    <argument>buildx</argument>
                    <argument>imagetools</argument>
                    <argument>create</argument>
                    <!-- Version tag names: patch, minor, major, latest -->
                    <argument>--tag</argument>
                    <argument>${docker.image.name}:${project.version}</argument>
                    <argument>--tag</argument>
                    <argument>${docker.image.name}:${parsedVersion.majorVersion}.${parsedVersion.minorVersion}</argument>
                    <argument>--tag</argument>
                    <argument>${docker.image.name}:${parsedVersion.majorVersion}</argument>
                    <argument>--tag</argument>
                    <argument>${docker.image.name}:latest</argument>
                    <!-- Sources: the per-architecture images published above -->
                    <argument>${docker.image.name}:${project.version}-amd64</argument>
                    <argument>${docker.image.name}:${project.version}-arm64</argument>
                  </arguments>
                </configuration>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
              <docker>
                <publishRegistry>
                  <username>${env.DOCKERHUB_USERNAME}</username>
                  <password>${env.DOCKERHUB_PASSWORD}</password>
                </publishRegistry>
              </docker>
            </configuration>
            <executions>
              <execution>
                <id>build-image-amd64</id>
                <goals>
                  <goal>build-image</goal>
                </goals>
                <phase>package</phase>
                <configuration>
                  <image>
                    <name>${docker.image.name}:${project.version}-amd64</name>
                    <!--
                      Reference the amd64-only local tag pulled/tagged above, not the shared
                      bellsoft/buildpacks.builder:musl tag: with pullPolicy IF_NOT_PRESENT, both
                      architectures resolving the same tag would cause whichever build-image
                      execution runs second to silently reuse the platform pulled by the first.
                    -->
                    <builder>bellsoft/buildpacks.builder:musl-amd64</builder>
                    <runImage>${docker.run.image.name}:musl-amd64</runImage>
                    <imagePlatform>linux/amd64</imagePlatform>
                    <publish>true</publish>
                  </image>
                </configuration>
              </execution>
              <execution>
                <id>build-image-arm64</id>
                <goals>
                  <goal>build-image</goal>
                </goals>
                <phase>package</phase>
                <configuration>
                  <image>
                    <name>${docker.image.name}:${project.version}-arm64</name>
                    <!-- See build-image-amd64 above for why this must not use the shared tag. -->
                    <builder>bellsoft/buildpacks.builder:musl-arm64</builder>
                    <runImage>${docker.run.image.name}:musl-arm64</runImage>
                    <imagePlatform>linux/arm64</imagePlatform>
                    <publish>true</publish>
                  </image>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
</project>
