# client java (AsyncAPI)

Source: https://apigenie.pl/techdocs/reference/cli-generate_client_java_asyncapi

Generate a Java Kafka client library for the Kafka channels of an AsyncAPI document.

## Usage

```sh
apigenie-generator client java [...OPTIONS] INPUT_FILE
```

## Description

`apigenie-generator client java` generates from OpenAPI documents (see
[apigenie-generator client java](/techdocs/reference/cli-generate_client_java)) and from AsyncAPI 3.x
documents, telling them apart by their `openapi` or `asyncapi` field. Documents referencing other
files are bundled first.

From an AsyncAPI document it generates a Maven project of a plain Java 21 library on the Apache
Kafka client (`kafka-clients` 4.1.0), without Spring. Payloads are JSON (Jackson) and are
validated with Bean Validation. By default the client is the counterpart of the application the
document describes: it sends the messages of the application's `receive` operations and receives
those of its `send` operations. Set the `perspective` option to `application` to generate the
described application's side instead.
[apigenie-generator server spring](/techdocs/reference/cli-generate_server_spring_asyncapi) generates the
other side as a Spring Boot application. The two share their records, dispatch and header rules,
so they exchange the same records.

The generator writes the Kafka channels: the channels of servers with the `kafka` or
`kafka-secure` protocol, and channels with a Kafka binding in documents without servers. Channels
of other protocols are skipped and reported as info.

With `perspective: application`, the client of the Carpet Express Event Platform sends the
shipment events and requests labels:

```java
var options = new CarpetExpressEventsKafkaClientOptions()
    .bootstrapServers("localhost:9092")
    .replyTimeout(Duration.ofSeconds(10));
try (var client = new CarpetExpressEventsKafkaClient(options)) {
    client.sendShipmentDelivered(delivered).get();

    // request/reply: completes with the reply carrying the request's correlation id
    switch (client.requestLabelRender(request).get()) {
        case LabelRendered rendered -> System.out.println(rendered.payload().url());
        case LabelRenderFailed failed -> System.out.println(failed.payload().title());
    }
}
```

In the default perspective, it receives them and renders the labels:

```java
class LabelFarm implements RequestLabelRenderHandler {
    @Override
    public RequestLabelRenderReply onRenderLabel(RenderLabel message) {
        // sent to the labelRenderResults topic with the request's correlation id
        return new LabelRendered(message.key(), render(message.payload()), null);
    }
}

KafkaSubscription subscription = client.listenRequestLabelRender(new LabelFarm());
subscription.assigned().get(); // once the consumer group assigned partitions
```

The artifact id is `--package-name`, or the document's title followed by `-client`; the base
package is `base_package`, or the title's (e.g. `com.example.carpetexpresseventplatform.client`):

| Package | Contents |
| ------- | -------- |
| `model` | the records, enums and sealed interfaces of the schemas the payloads use, with Jackson and Bean Validation annotations |
| `messages` | a record per message (`key`, `payload`, `headers`), its headers record with the constant headers (`CE_TYPE`, ...), and a sealed interface per reply |
| `kafka` | `<Name>KafkaClient`, `<Name>KafkaClientOptions`, a handler interface per received operation, and the runtime: `KafkaJson`, `KafkaSubscription`, `KafkaReplies` |

- **The client.** `new <Name>KafkaClient(options)` creates a producer per sending and replying
  operation, with the operation's client id; `close()` closes the subscriptions, the reply
  consumer and the producers. The options default to the document's server, topics, client ids
  and group ids. They also take the reply client id and timeout (30 seconds by default), and
  producer and consumer properties that replace the client's (security settings, for example).
- **Sent operations.** `send<Message>(message)` validates the message, writes its key, its
  headers (constant headers such as the `ce_specversion` and `ce_type` of CloudEvents included)
  and its JSON payload to the operation's topic. It returns a `CompletableFuture<RecordMetadata>`.
- **Request/reply** with a reply channel: `<operation>(message)` and `<operation>(message,
  timeout)` set a new correlation id unless the message has one (in its header, at its payload
  pointer, or in `kafka_correlationId`) and send the request. They return a `CompletableFuture`
  of the reply, or of the sealed interface of the reply messages (`RequestLabelRenderReply`); the
  future fails with a `TimeoutException` when no reply arrives in time. One consumer, without a
  consumer group, receives the replies of all requests and matches them by correlation id.
- **Received operations.** `listen<Operation>(handler)` consumes the operation's topic on a
  virtual thread, in its consumer group (new groups start at the first record), until the
  returned `KafkaSubscription` or the client is closed. It commits the offsets of the records it
  handled.
  - The handler interface (`PublishShipmentEventsHandler`) has an `on<Message>` method per
    message, a default `onUnknown` and a default `onError`.
  - Several messages are told apart by a header or payload property with a distinct constant
    value (e.g. `ce_type`). Otherwise the first message whose payload reads strictly and is
    valid is chosen.
  - Records that aren't valid messages, and exceptions of the handler, go to `onError`.
  - A reply returned by the handler is sent to the reply topic with the request's correlation id.

Not supported, and reported as warnings: Avro payloads and payloads of other non-JSON schema
formats (the messages are skipped, and operations left without a message too), reply addresses
given by runtime expressions, and Kafka topics with parameters. Security (SASL/SCRAM, mTLS) is not
configured: set `security.protocol`, `sasl.*` and `ssl.*` with `producerProperty` and
`consumerProperty`.

## Extensions

| Extension | Effect |
| --------- | ------ |
| `x-apigenie.codegen.name` | name of the class of a schema |
| `x-apigenie.codegen.disabled` | `true` on a channel or an operation leaves it out (with the operations of a channel left out) |

The extensions can live in an overlay for this target instead of in the document, e.g.
`java.overlay.yaml`:

```yaml
overlay: 1.0.0
info:
  title: Java names
  version: 1.0.0
actions:
  - target: $.components.schemas.Order
    update:
      x-apigenie:
        codegen:
          name: PurchaseOrder
```

```sh
apigenie-generator client java asyncapi.yaml --overlay java.overlay.yaml -o out
```

## Options

### -c, --config

Path to the configuration file to use. See the "Configuration" section for more information.

### --overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the
extensions of the "Extensions" section. Can be repeated: the overlays apply in order, after those
listed under `commands.generate.client_java.overlay` in the configuration file (with paths relative
to it). Warnings and errors about a value an overlay set point to the overlay. The overlays apply to
the AsyncAPI document bundled with the files it references.

### --strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated)
instead of a warning (`OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.`).

### -o, --output

Directory where the Maven project is generated.

### --package-name

The artifact id, by default the document's title followed by `-client`. Its base, without
`-client`, is the base of default group and client ids.

### --package-version

Version of the artifact; defaults to the version of the API.

### --library, -p, --property

Apply to OpenAPI documents only; using them with an AsyncAPI document is an error.

## Configuration

The options are read from the `asyncapi` section of the configuration of `apigenie-generator client java`;
its other keys configure the generation from OpenAPI documents.

```yaml
version: '1.0'
commands:
  generate:
    client_java:
      asyncapi:
        base_package: com.acme.events
        group_id: com.acme
        perspective: counterpart
```

| Option | Description |
| ------ | ----------- |
| `base_package` | Package of the generated code (default: from the title, followed by `.client`) |
| `group_id` | Maven `groupId` (default: the base package) |
| `java_version` | Java release, from 21 (default: 21; the client needs virtual threads) |
| `bean_validation` | Bean Validation annotations on the models, and validation of sent and received messages (default: `true`) |
| `perspective` | `counterpart` (default): the counterpart of the described application; `application`: the described application, sending what it sends |
