Step 5 of 8

Part 4: Consumer Validator (swim-ffice-consumer-validator)

The consumer-validator is a lightweight Quarkus application that simulates an external SWIM provider. It provides:

  • A mock Subscription Manager REST API (/swim/v1/subscriptions, /swim/v1/topics)
  • An ActiveMQ Artemis AMQP broker for message delivery
  • An event generator that periodically publishes FF-ICE XML samples to the broker

4.1 Create the project

Create the project directory structure:

swim-ffice-consumer-validator/
  pom.xml
  src/main/docker/Containerfile.jvm
  src/main/resources/application.properties
  src/main/resources/events/               (XML samples go here, see 4.3)
  src/main/java/com/github/swim_developer/validator/ffice/infrastructure/rest/
    SubscriptionManagerResource.java
    ValidatorTopicConfig.java

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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.github.swim-developer</groupId>
        <artifactId>swim-validators</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath/>
    </parent>

    <artifactId>swim-ffice-consumer-validator</artifactId>
    <name>SWIM FF-ICE Consumer Validator</name>
    <description>Mock AISP/EAD for FF-ICE consumer development and testing</description>

    <dependencies>
        <dependency>
            <groupId>com.github.swim-developer</groupId>
            <artifactId>swim-validator-consumer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.platform.version}</version>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <parameters>true</parameters>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Containerfile.jvm

This Dockerfile is used by the compose.yml to build the validator image locally:

FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.22

ENV LANGUAGE='en_US:en'

COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=185 target/quarkus-app/*.jar /deployments/
COPY --chown=185 target/quarkus-app/app/ /deployments/app/
COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/
COPY --chown=185 src/main/resources/events/ /opt/events/

EXPOSE 8080 8443

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" ]

4.2 Implement the Subscription Manager REST API

The swim-validator-consumer library does not ship JAX-RS endpoints. You must create two classes.

SubscriptionManagerResource.java

This class exposes the SWIM Subscription Manager API that the consumer expects. It delegates to ManageSubscriptionPort from swim-validator-core.

Important: The consumer sends queueName (camelCase) and expects queueName in the response. The validator-core uses queue (snake_case). The toSubscriptionDto method handles this mapping. If the consumer sends an empty queueName, pass null to CreateSubscriptionCommand so the validator generates one automatically.

package com.github.swim_developer.validator.ffice.infrastructure.rest;

import com.github.swim_developer.validator.consumer.domain.model.CreateSubscriptionCommand;
import com.github.swim_developer.validator.consumer.domain.port.in.ManageSubscriptionPort;
import com.github.swim_developer.validator.core.domain.model.QualityOfService;
import com.github.swim_developer.validator.core.domain.model.SubscriptionResponse;
import com.github.swim_developer.validator.core.domain.model.SubscriptionStatus;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.util.List;
import java.util.Map;

@Slf4j
@Path("/swim/v1")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SubscriptionManagerResource {

    @Inject
    ManageSubscriptionPort subscriptionPort;

    @Inject
    ValidatorTopicConfig topicConfig;

    @POST
    @Path("/subscriptions")
    public Response createSubscription(Map<String, Object> request) {
        String topic = (String) request.getOrDefault("topic", "ffice.v1");
        String description = (String) request.getOrDefault("description", "");

        String rawQueue = (String) request.get("queueName");
        String queueName = (rawQueue != null && !rawQueue.isBlank()) ? rawQueue : null;
        var command = new CreateSubscriptionCommand(
                topic, queueName, QualityOfService.AT_LEAST_ONCE, true,
                List.of(), List.of(), List.of(), null, null, description, null);
        SubscriptionResponse sub = subscriptionPort.createSubscription(command);

        log.info("Subscription created: id={}, queue={}", sub.subscriptionId(), sub.queue());
        return Response.status(201).entity(toSubscriptionDto(sub)).build();
    }

    @GET
    @Path("/subscriptions")
    public Response listSubscriptions(
            @QueryParam("queueName") String queueName,
            @QueryParam("subscriptionStatus") String status) {
        SubscriptionStatus subStatus = status != null ? SubscriptionStatus.valueOf(status) : null;
        List<SubscriptionResponse> subs = subscriptionPort.listSubscriptions(queueName, subStatus);
        return Response.ok(subs.stream().map(this::toSubscriptionDto).toList()).build();
    }

    @GET
    @Path("/subscriptions/{subscriptionId}")
    public Response getSubscription(@PathParam("subscriptionId") String subscriptionId) {
        return subscriptionPort.getSubscriptionDetails(subscriptionId)
                .map(sub -> Response.ok(toSubscriptionDto(sub)).build())
                .orElse(Response.status(404).build());
    }

    @PUT
    @Path("/subscriptions/{subscriptionId}")
    public Response updateSubscriptionStatus(
            @PathParam("subscriptionId") String subscriptionId,
            Map<String, String> body) {
        String newStatus = body.getOrDefault("subscription_status",
                body.getOrDefault("subscriptionStatus", "ACTIVE"));
        SubscriptionStatus status = SubscriptionStatus.valueOf(newStatus);
        SubscriptionResponse sub = subscriptionPort.updateSubscriptionStatus(subscriptionId, status);
        return Response.ok(toSubscriptionDto(sub)).build();
    }

    @DELETE
    @Path("/subscriptions/{subscriptionId}")
    public Response deleteSubscription(@PathParam("subscriptionId") String subscriptionId) {
        subscriptionPort.deleteSubscription(subscriptionId);
        return Response.noContent().build();
    }

    @PUT
    @Path("/subscriptions/{subscriptionId}/renew")
    public Response renewSubscription(@PathParam("subscriptionId") String subscriptionId) {
        return subscriptionPort.renewSubscription(subscriptionId)
                .map(sub -> Response.ok(toSubscriptionDto(sub)).build())
                .orElse(Response.status(404).build());
    }

    @GET
    @Path("/topics")
    public Response listTopics() {
        var topics = topicConfig.topicSummaries().stream()
                .map(t -> Map.of(
                        "topicId", t.id(),
                        "title", t.name(),
                        "description", t.description()))
                .toList();
        return Response.ok(Map.of("topics", topics)).build();
    }

    @GET
    @Path("/topics/{topicId}")
    public Response getTopicDetails(@PathParam("topicId") String topicId) {
        return topicConfig.topicSummaries().stream()
                .filter(t -> t.id().equals(topicId))
                .findFirst()
                .map(t -> Response.ok(Map.of(
                        "topicId", t.id(),
                        "topicName", t.name(),
                        "description", t.description(),
                        "publisherState", "ACTIVE")).build())
                .orElse(Response.status(404).build());
    }

    @GET
    @Path("/features")
    @Produces(MediaType.APPLICATION_XML)
    public Response getFeatures(
            @QueryParam("typeName") String typeName,
            @QueryParam("filter") String filter,
            @QueryParam("validTime") String validTime) {
        return Response.ok("<FeatureCollection/>").build();
    }

    private Map<String, Object> toSubscriptionDto(SubscriptionResponse sub) {
        return Map.of(
                "subscriptionId", sub.subscriptionId().toString(),
                "subscriptionStatus", sub.subscriptionStatus().name(),
                "queueName", sub.queue() != null ? sub.queue() : "",
                "subscriptionEnd", sub.subscriptionEnd() != null
                        ? sub.subscriptionEnd().toString()
                        : Instant.now().plusSeconds(86400).toString());
    }
}

ValidatorTopicConfig.java

package com.github.swim_developer.validator.ffice.infrastructure.rest;

import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty;

import java.util.List;

@ApplicationScoped
public class ValidatorTopicConfig {

    private final List<TopicEntry> topics;

    public ValidatorTopicConfig(
            @ConfigProperty(name = "validator.topic.id", defaultValue = "ffice-v1") String topicId,
            @ConfigProperty(name = "validator.topic.name", defaultValue = "ffice.v1") String topicName,
            @ConfigProperty(name = "validator.topic.description", defaultValue = "FF-ICE Flight Information messages (FIXM 4.3)") String topicDescription) {
        this.topics = List.of(new TopicEntry(topicId, topicName, topicDescription));
    }

    public List<TopicEntry> topicSummaries() {
        return topics;
    }

    public record TopicEntry(String id, String name, String description) {}
}

4.3 Configure the event generator

The validator needs sample FF-ICE messages to publish periodically to the AMQP broker. These are fictitious XML files that follow the FIXM 4.3 / FF-ICE 1.1 XSD structure. They exist solely to validate the end-to-end flow: message delivery, JAXB unmarshalling, field extraction, Kafka routing, and MongoDB persistence. They are not real operational data.

Create them in src/main/resources/events/. The Containerfile.jvm copies this folder to /opt/events/ inside the container, and the event.generator.events.path property points there.

Create 7 files, one per FF-ICE message type:

src/main/resources/events/filed-flight-plan.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>LFPG</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>LPPT</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>TAP123</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T07:00:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">f47ac10b-58cc-4372-a567-0e02b2c3d479</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T08:00:00.000Z</ffice:timestamp>
    <ffice:type>FILED_FLIGHT_PLAN</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">a1b2c3d4-e5f6-4890-abcd-ef1234567890</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/flight-plan-update.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>EGLL</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>EHAM</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>KLM456</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T08:00:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">e47bc20c-68dd-4483-b678-1f13c4d5e580</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T09:00:00.000Z</ffice:timestamp>
    <ffice:type>FLIGHT_PLAN_UPDATE</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">b2c3d4e5-f6a7-4901-bcde-f12345678901</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/flight-departure.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>EDDF</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>LEMD</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>IBE789</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T09:00:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">d58cd31d-79ee-4594-a789-2024d5e6f691</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T10:00:00.000Z</ffice:timestamp>
    <ffice:type>FLIGHT_DEPARTURE</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">c3d4e5f6-a7b8-4012-8def-123456789012</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/flight-arrival.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:arrivalAerodrome>
                <fb:locationIndicator>LIRF</fb:locationIndicator>
            </fx:arrivalAerodrome>
            <fx:destinationAerodrome>
                <fb:locationIndicator>LIRF</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>LFPG</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>AFR101</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T10:00:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">a69de42e-8aff-4605-a890-3035e6f70702</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T11:00:00.000Z</ffice:timestamp>
    <ffice:type>FLIGHT_ARRIVAL</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">d4e5f6a7-b8c9-4123-9efa-234567890123</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/flight-cancellation.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>ESSA</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>EKCH</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>EZY321</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T08:30:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">d92a075b-bd22-4938-a123-606808090035</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T09:30:00.000Z</ffice:timestamp>
    <ffice:type>FLIGHT_CANCELLATION</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">a7b8c9d0-e1f2-4456-abcd-567890123456</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/planning-status.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>EBBR</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>EGLL</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>BAW202</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T06:00:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">b70ef53f-9b00-4716-a901-4046f7080813</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T07:00:00.000Z</ffice:timestamp>
    <ffice:type>PLANNING_STATUS</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">e5f6a7b8-c9d0-4234-8fab-345678901234</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

src/main/resources/events/filing-status.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  FICTITIOUS DATA - This file contains synthetic data created exclusively for
  development and testing purposes. It has no relationship whatsoever with any
  real operational environment, airline, flight, or air navigation service provider.

  Created for the swim-developer project under the Apache License 2.0.
  https://github.com/swim-developer
-->
<ffice:FficeMessage xmlns:ffice="http://www.fixm.aero/app/ffice/1.1"
                    xmlns:fx="http://www.fixm.aero/flight/4.3"
                    xmlns:fb="http://www.fixm.aero/base/4.3">
    <ffice:flight>
        <fx:arrival>
            <fx:destinationAerodrome>
                <fb:locationIndicator>EDDM</fb:locationIndicator>
            </fx:destinationAerodrome>
        </fx:arrival>
        <fx:departure>
            <fx:departureAerodrome>
                <fb:locationIndicator>LSZH</fb:locationIndicator>
            </fx:departureAerodrome>
        </fx:departure>
        <fx:flightIdentification>
            <fx:aircraftIdentification>SWR303</fx:aircraftIdentification>
            <fx:gufi codeSpace="urn:uuid" creationTime="2026-05-06T07:30:00Z" namespaceDomain="FULLY_QUALIFIED_DOMAIN_NAME" namespaceIdentifier="swim-developer.github.io">c81f064f-ac11-4827-b012-505708090924</fx:gufi>
        </fx:flightIdentification>
    </ffice:flight>
    <ffice:timestamp>2026-05-06T08:30:00.000Z</ffice:timestamp>
    <ffice:type>FILING_STATUS</ffice:type>
    <ffice:uniqueMessageIdentifier codeSpace="urn:uuid">f6a7b8c9-d0e1-4345-9abc-456789012345</ffice:uniqueMessageIdentifier>
</ffice:FficeMessage>

The event generator (scheduled task from swim-validator-consumer) reads these files and publishes them to the Artemis AMQP topic at a configurable interval.

4.4 Configure application.properties

Place this file at src/main/resources/application.properties:

# =============================================================================
# SWIM FF-ICE Consumer Validator
# =============================================================================
quarkus.application.name=swim-ffice-consumer-validator

# =============================================================================
# AMQP Broker Connection (Artemis inside compose network)
# =============================================================================
amqp.broker.host=${AMQP_BROKER_HOST:localhost}
amqp.broker.port=${AMQP_BROKER_PORT:5672}
amqp.broker.username=${AMQP_BROKER_USERNAME:admin}
amqp.broker.password=${AMQP_BROKER_PASSWORD:admin}
amqp.broker.ssl.enabled=${AMQP_BROKER_SSL_ENABLED:false}

# =============================================================================
# MariaDB Persistence (subscription state)
# =============================================================================
quarkus.datasource.db-kind=mariadb
quarkus.datasource.jdbc.url=jdbc:mariadb://${MARIADB_HOST:localhost}:${MARIADB_PORT:3306}/${MARIADB_DATABASE:swim_ffice_consumer_validator}
quarkus.datasource.username=${MARIADB_USERNAME:swim}
quarkus.datasource.password=${MARIADB_PASSWORD:swim}
quarkus.hibernate-orm.database.generation=update

# =============================================================================
# Event Generator
# =============================================================================
event.generator.enabled=${EVENT_GENERATOR_ENABLED:true}
event.generator.schedule=${EVENT_GENERATOR_SCHEDULE:0 */1 * * * ?}
event.generator.events.path=${EVENT_GENERATOR_EVENTS_PATH:/opt/events}
event.generator.randomize-dates=true

# =============================================================================
# Heartbeat Publisher
# =============================================================================
heartbeat.publisher.enabled=${HEARTBEAT_PUBLISHER_ENABLED:true}
heartbeat.publisher.interval=${HEARTBEAT_PUBLISHER_INTERVAL:15s}

# =============================================================================
# Topics (FF-ICE service)
# =============================================================================
validator.topic.id=ffice-v1
validator.topic.name=ffice.v1
validator.topic.description=FF-ICE Flight Information messages (FIXM 4.3)
validator.service.default-topic=ffice.v1
validator.queue.prefix=FFICE.v1
swim.provider.name=SWIM-FFICE-Validator

# =============================================================================
# HTTP
# =============================================================================
quarkus.http.port=8080
quarkus.http.insecure-requests=${QUARKUS_HTTP_INSECURE_REQUESTS:enabled}

# =============================================================================
# TLS (HTTPS for SM REST API)
# =============================================================================
quarkus.http.ssl.certificate.files=/certs/server/tls.crt
quarkus.http.ssl.certificate.key-files=/certs/server/tls.key
quarkus.tls.trust-all=${QUARKUS_TLS_TRUST_ALL:false}

# =============================================================================
# Jandex Index
# =============================================================================
quarkus.index-dependency.swim-validator-consumer.group-id=com.github.swim-developer
quarkus.index-dependency.swim-validator-consumer.artifact-id=swim-validator-consumer
quarkus.index-dependency.swim-validator-core.group-id=com.github.swim-developer
quarkus.index-dependency.swim-validator-core.artifact-id=swim-validator-core

# =============================================================================
# OpenAPI
# =============================================================================
quarkus.smallrye-openapi.path=/openapi
quarkus.swagger-ui.path=/swagger-ui
quarkus.swagger-ui.always-include=true
mp.openapi.extensions.smallrye.info.title=SWIM FF-ICE Consumer Validator API
mp.openapi.extensions.smallrye.info.version=1.0.0

# =============================================================================
# Logging
# =============================================================================
quarkus.log.level=INFO
quarkus.log.category."com.github.swim_developer".level=INFO
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%c] (%t) %s%e%n

4.5 Build

From the swim-ffice-consumer-validator directory:

./mvnw clean package -DskipTests        # Linux / macOS
mvnw.cmd clean package -DskipTests      # Windows