
# Hello World [Java]

## Introduction

The Java binding loads the prebuilt `libazul` native library through
[JNA](https://github.com/java-native-access/jna). You write ordinary Java — a plain
data class, a typed `LayoutCallback` that returns a `Dom`, and the wrapper-class
`App` API — and the generated wrappers handle the FFI. No manual byte splicing, no
`Pointer` arithmetic in your code.

## Installation

You need **JDK 17+**, **Maven**, **JNA 5.14+**, and the native `libazul` library.

Azul is not on Maven Central, but there is a self-hosted maven2 repository
served from azul.rs (generated by the release CI). Add it next to Maven
Central and depend on `rs.azul:azul` (the jar bundles the `com.azul`
bindings plus a Linux x86-64 `libazul.so` as a JNA resource; on other
platforms also do step 1 below so JNA finds the native library):

```xml
<repositories>
  <repository>
    <id>azul-rs</id>
    <url>https://azul.rs/ui/maven</url>
  </repository>
</repositories>
<dependencies>
  <dependency>
    <groupId>rs.azul</groupId>
    <artifactId>azul</artifactId>
    <version>0.2.0</version>
  </dependency>
</dependencies>
```

Alternatively, install manually. Only JNA itself
(`net.java.dev.jna:jna:5.14.0`) comes from Maven Central as usual.

1. Download the native library from the
   [release page](https://azul.rs/ui/release/0.2.0) (`libazul.dylib`
   / `libazul.so` / `azul.dll`) and keep it in your working directory or pass
   `-Djna.library.path=.`.
2. Download the generated `com.azul` wrapper sources and unpack them into
   your source tree:

   ```sh
   wget https://azul.rs/ui/release/0.2.0/azul-java.zip
   unzip azul-java.zip -d src/main/java/com/azul/
   ```

## Simple "Counter" Example

```java
package com.azul;

import com.sun.jna.Pointer;

public final class HelloWorld {

    // Plain data class - the "single source of truth" for app state.
    public static final class MyDataModel {
        public int counter;
        public MyDataModel(int counter) { this.counter = counter; }
    }

    private static final MyDataModel MODEL = new MyDataModel(5);

    // Click callback. refanyGet recovers your object from the handle; write the
    // Update value back through the out-pointer.
    private static final AzulNativeManaged.ButtonOnClickCallbackInvokerCallback ON_CLICK =
        (long id, Pointer dataPtr, Pointer infoPtr, Pointer outPtr) -> {
            Object m = AzulHostInvoker.refanyGet(dataPtr);
            int result = Update.DoNothing.value;
            if (m instanceof MyDataModel) {
                ((MyDataModel) m).counter += 1;
                result = Update.RefreshDom.value;
            }
            outPtr.setInt(0, result);
        };

    // Typed layout callback: returns a Dom directly. The host-invoker bridge
    // splices the Dom bytes into libazul's out-pointer for you.
    private static final AzulHostInvoker.LayoutCallback LAYOUT =
        (long id, Pointer dataPtr, Pointer infoPtr) -> {
            Object recovered = AzulHostInvoker.refanyGet(dataPtr);
            if (!(recovered instanceof MyDataModel)) {
                return Dom.createBody();
            }
            MyDataModel m = (MyDataModel) recovered;
            Dom label = Dom.createDiv()
                .withCss("font-size: 32px;")
                .withChild(Dom.createText(String.valueOf(m.counter)));
            Dom buttonDom = Button.create("Increase counter")
                .withButtonType(ButtonType.Primary.value)
                .onClick(m, ON_CLICK)
                .dom();
            return Dom.createBody()
                .withChild(label)
                .withChild(buttonDom);
        };

    public static void main(String[] args) {
        // try-with-resources disposes the App (C-side delete) on exit.
        try (App app = App.create(AzulHostInvoker.refanyWrap(MODEL), AppConfig.create())) {
            app.run(WindowCreateOptions.create(LAYOUT));
        }
    }
}
```

Four things to notice.

- **`AzulHostInvoker.refanyWrap` / `refanyGet`** — your `MyDataModel` is wrapped once
  and the same instance is handed back to every callback. Use `instanceof` to guard
  the cast and fall back to `Dom.createBody()` / `Update.DoNothing` on mismatch.
- **Typed `LayoutCallback` SAM** — returns a `Dom` directly; the bridge handles the
  byte-splice into the native out-pointer. Click handlers use the event's typed SAM
  (`ButtonOnClickCallbackInvokerCallback` for `Button.onClick`) and write the
  `Update` int via `outPtr.setInt(0, ...)`.
- **Wrapper-class fluent API** — `Dom.createBody().withChild(...)` and
  `Button.create(label).withButtonType(...).onClick(data, fn).dom()`. `AzulString`
  decodes to `java.lang.String` via `.toString()`.
- **`try (App app = ...)`** releases native memory deterministically — `close()` calls
  the C-side `delete`.

## Build and run

Use the ready-made Maven project from the repository —
[`examples/java/pom.xml`](https://github.com/fschutt/azul/blob/master/examples/java/pom.xml)
— as your project pom. It bundles JNA into the output jar via
`maven-shade-plugin` and sets `Main-Class: com.azul.HelloWorld` in the
manifest, so a plain `java -jar` works with no manual classpath:

```sh
mvn package
# macOS — -XstartOnFirstThread is REQUIRED so libazul's NSApplication loop
# pumps on the JVM main thread.
java -XstartOnFirstThread -Djna.library.path=. -jar target/hello-world-1.0.0.jar
```

On Linux/Windows drop `-XstartOnFirstThread`. `-Djna.library.path=.` points
JNA at the directory holding `libazul.dylib` / `libazul.so` / `azul.dll`.

You should see the window pictured on the [hello-world landing page](../hello-world.md).
Click the button: the counter increments and the layout callback re-runs.

## Common errors

- **`UnsatisfiedLinkError` / library not found** — the native library isn't on
  `-Djna.library.path` / `DYLD_LIBRARY_PATH` / `LD_LIBRARY_PATH`.
- **Window never appears / instant crash on macOS** — you omitted
  `-XstartOnFirstThread`. Cocoa requires the event loop on the main thread.
- **Counter does not advance** — the click handler wrote `Update.DoNothing.value`.
  Write `Update.RefreshDom.value` to `outPtr` after mutating.

## Coming Up Next

- [Application Architecture](../architecture.md) — architecting a larger Azul application
- [Document Object Model](../dom.md) — the Dom tree: node types, hierarchy, and CSS
- [Hello World [Kotlin]](kotlin.md)
