> ## Documentation Index
> Fetch the complete documentation index at: https://repost.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Java Code Generation

> How the Repost Maven plugin generates your Java client: the generator block, the repost:check CI gate, engine pinning, and multi-module aggregation.

export const RepostHighlight = () => {
  useEffect(() => {
    const colors = {
      comment: ['#116329', '#6A9955'],
      constant: ['#0550AE', '#9CDCFE'],
      keyword: ['#CF222E', '#569CD6'],
      name: ['#953800', '#4EC9B0'],
      string: ['#0A3069', '#CE9178'],
      type: ['#0550AE', '#4EC9B0'],
      variable: ['#1F2328', '#9CDCFE']
    };
    const tokenPattern = /"[^"\n]*"|@[A-Za-z_]\w*|\b(?:String|Int|Float|Boolean|DateTime|Json)\b|\b(?:true|false)\b|\b\d+(?:\.\d+)?\b|\b(?:now|uuid|cuid)(?=\s*\()|\b[A-Z]\w*\b|\b[A-Za-z_]\w*\b/g;
    const append = (line, text, color) => {
      if (!text) return;
      if (!color) {
        line.append(text);
        return;
      }
      const span = document.createElement('span');
      span.textContent = text;
      span.style.color = color[0];
      span.style.setProperty('--shiki-dark', color[1]);
      line.append(span);
    };
    const commentStart = text => {
      let quoted = false;
      for (let index = 0; index < text.length - 1; index += 1) {
        if (text[index] === '"' && text[index - 1] !== '\\') quoted = !quoted;
        if (!quoted && text[index] === '/' && text[index + 1] === '/') return index;
      }
      return -1;
    };
    const highlightCode = code => {
      if (code.dataset.repostHighlighted !== undefined || code.querySelector('span[style*="--shiki-dark"]')) {
        return;
      }
      code.dataset.repostHighlighted = '';
      const source = code.textContent ?? '';
      const fragment = document.createDocumentFragment();
      for (const text of source.split('\n')) {
        const line = document.createElement('span');
        line.className = 'line';
        const declaration = text.match(/^(\s*)(generator|model|enum|type|event)(\s+)([A-Za-z_]\w*)(.*)$/);
        if (declaration) {
          append(line, declaration[1]);
          append(line, declaration[2], colors.keyword);
          append(line, declaration[3]);
          append(line, declaration[4], colors.name);
          append(line, declaration[5]);
        } else {
          const start = commentStart(text);
          const codeText = start === -1 ? text : text.slice(0, start);
          const leading = codeText.match(/^(\s*)([A-Za-z_]\w*)(?=\s+(?:@|[A-Z]))/);
          const bare = codeText.match(/^(\s*)([A-Za-z_]\w*)(\s*)$/);
          let offset = 0;
          for (const match of codeText.matchAll(tokenPattern)) {
            append(line, codeText.slice(offset, match.index));
            const token = match[0];
            let color = colors.constant;
            if (token.startsWith('"')) color = colors.string; else if (token.startsWith('@') || (/^(now|uuid|cuid)$/).test(token)) color = colors.name; else if ((/^(String|Int|Float|Boolean|DateTime|Json)$/).test(token)) color = colors.type; else if (leading && match.index === leading[1].length) color = colors.variable; else if (bare && match.index === bare[1].length) color = colors.constant; else if ((/^[A-Z]/).test(token)) color = colors.name; else if ((/^[A-Za-z_]/).test(token)) color = colors.variable;
            append(line, token, color);
            offset = match.index + token.length;
          }
          append(line, codeText.slice(offset));
          if (start !== -1) append(line, text.slice(start), colors.comment);
        }
        fragment.append(line, '\n');
      }
      code.replaceChildren(fragment);
    };
    const highlightRepostBlocks = () => {
      for (const block of document.querySelectorAll('.code-block')) {
        const code = block.querySelector('pre code');
        const filename = block.querySelector('[data-component-part="code-block-header-filename"] [title$=".repost"]');
        const looksLikeRepost = (/^\s*(generator|model|enum|type|event)\s+[A-Za-z_]\w*\s*\{/m).test(code?.textContent ?? '');
        if (code && (filename || looksLikeRepost)) highlightCode(code);
      }
    };
    let frame;
    const schedule = () => {
      if (frame !== undefined) return;
      frame = requestAnimationFrame(() => {
        frame = undefined;
        highlightRepostBlocks();
      });
    };
    const observer = new MutationObserver(schedule);
    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
    });
    schedule();
    return () => {
      observer.disconnect();
      if (frame !== undefined) cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<RepostHighlight />

The Maven plugin runs the schema engine as part of your build and owns two output trees: generated sources and a discovery resource. This page is the reference for that machinery; for the end-to-end setup, start at the [quickstart](/docs/send/java/quickstart).

## The generator block

A Java `generator` block sets four things the other languages don't need:

```repost repost/schema.repost theme={"languages":{"custom":["/languages/repost.json"]}}
generator javaSdk {
  language       = "java"
  output         = "../target/generated-sources/repost"
  resourceOutput = "../target/generated-resources/repost"
  packageName    = "com.example.repost"
  clientName     = "RepostClient"
}
```

| Field            | What it owns                                                                                                                                                                                          |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `output`         | The **source** root. Generated `.java` files are written under the `packageName` subdirectory here. Repost owns this tree — it replaces its own files atomically and never touches anything else.     |
| `resourceOutput` | The **resource** root. One `client.json` registry per client is written to `META-INF/repost/generated-clients/v1/<id>/` here, so a framework or the aggregator can discover the client at build time. |
| `packageName`    | The Java package for the generated models, `Webhooks` tree, and client.                                                                                                                               |
| `clientName`     | The generated client class name (`RepostClient` above). Pick a distinct name per schema when a build hosts more than one.                                                                             |

Source and resources are **one transaction**: a regeneration replaces both together or restores the previous tree — a crash never leaves a half-written client or a stale registry. Point `output`/`resourceOutput` at build-output directories (as above); they are regenerated, not committed.

## The engine

The plugin resolves and runs a signed, platform-specific schema engine (`sh.repost:repost-schema-engine:0.9.0`) — nothing else to install. Pin a different engine build with `<engineVersion>` on the plugin (or `-Drepost.engineVersion=…`); the checksum and signature are verified before the engine runs, and a mismatch fails the build.

## Generate and check

The plugin binds to the build. Two goals:

<CodeGroup>
  ```bash Generate (Maven) theme={"languages":{"custom":["/languages/repost.json"]}}
  ./mvnw repost:generate
  ```

  ```bash Verify without mutating (Maven) theme={"languages":{"custom":["/languages/repost.json"]}}
  ./mvnw repost:check
  ```

  ```bash Generate (CLI) theme={"languages":{"custom":["/languages/repost.json"]}}
  repost schema generate
  ```
</CodeGroup>

`repost:generate` runs in the `generate-sources` phase, so `mvn compile` already has your client. `repost:check` regenerates into an isolated directory, hashes the result, and fails if it differs from the committed output **without writing to your source tree** — the CI gate that proves generated code is in sync. The `repost` CLI generates the identical tree from the same schema, so a teammate without Maven can regenerate too.

## `GENERATE` versus multi-module `AGGREGATE_ONLY`

Every module runs in one of two modes (`<schemaMode>`; default `GENERATE`):

* **`GENERATE`** — run the engine on this module's schema, emit the client and its registry, and aggregate any client registries found on the compile classpath. This is what a single-module app or a schema library uses.
* **`AGGREGATE_ONLY`** — run **no** engine and generate **no** client. Only merge the registries published by dependency modules into one application registry. An application module that consumes clients from several schema libraries uses this so it never regenerates code it doesn't own.

In a multi-module build, each **schema library** stays `GENERATE` (it owns and publishes one client), and the **application** module sets `AGGREGATE_ONLY`:

```xml Application module — pom.xml theme={"languages":{"custom":["/languages/repost.json"]}}
<configuration>
  <schemaMode>AGGREGATE_ONLY</schemaMode>
  <integration>SPRING_BOOT</integration>
</configuration>
```

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Schema workflow" icon="braces" href="/docs/cli/schema" cta="Schema" arrow="true">
    Version the schema with migrations and deploy it to your environment.
  </Card>

  <Card title="Configuration" icon="sliders-horizontal" href="/docs/send/java/configuration" cta="Configure" arrow="true">
    Credentials, timeouts, retries, and transport options with production defaults.
  </Card>

  <Card title="Frameworks" icon="boxes" href="/docs/send/java/frameworks" cta="Inject" arrow="true">
    Spring Boot, Jakarta CDI, and the framework-neutral core pattern.
  </Card>
</Columns>
