Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions ci/run_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ install_jdks() {
done
}

run_graalvm_tests() {
run_graalvm_test() {
local main_class="$1"
local java_version
local java_major
Expand All @@ -102,8 +102,8 @@ run_graalvm_tests() {
-Dmaven.test.skip=true \
-Dmaven.source.skip=true \
-Dmaven.javadoc.skip=true
echo "Start to build GraalVM JPMS native image for $main_class"
cd "$ROOT"/integration_tests/graalvm_tests
echo "Start to build GraalVM JPMS native image for $main_class"
mvn -DmainClass="$main_class" -DskipTests=true -Dassembly.skipAssembly=true \
--no-transfer-progress -Pnative-module clean package
echo "Built GraalVM JPMS native image"
Expand All @@ -113,11 +113,11 @@ run_graalvm_tests() {
}

graalvm_test() {
run_graalvm_tests org.apache.fory.graalvm.Main
run_graalvm_test org.apache.fory.graalvm.Main
}

graalvm_json_tests() {
run_graalvm_tests org.apache.fory.graalvm.ForyJsonExample
run_graalvm_test org.apache.fory.graalvm.ForyJsonExample
}

jdk25_access_options() {
Expand Down
125 changes: 72 additions & 53 deletions docs/guide/java/graalvm-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,8 @@ compilation is unavailable.

## Fory JSON

Fory JSON uses a separate Native Image workflow. Add the Fory annotation processor to the
application compiler path:

```xml
<annotationProcessorPaths>
<path>
<groupId>org.apache.fory</groupId>
<artifactId>fory-annotation-processor</artifactId>
<version>${fory.version}</version>
</path>
</annotationProcessorPaths>
```

Then add `@JsonType` to each concrete object model that the native executable reads or writes:
Fory JSON has its own Native Image Feature and does not use the Fory annotation processor. Add
`@JsonType` to each reachable concrete object model that the native executable reads or writes:

```java
import org.apache.fory.json.ForyJson;
Expand All @@ -80,7 +68,54 @@ public class JsonExample {
}
```

The processor also supports Fory JSON Mixins for models that cannot be modified:
This is sufficient for correct native execution. During image construction, Fory JSON retains the
model metadata and prepares its field, property, creator, record, and `JsonAnySetter` access. At
runtime, `ForyJson.builder().build()` can therefore use interpreted codecs without application
reflection configuration, package exports or opens, or build-time initialization.

To include generated codecs for a configuration, return that completed configuration from a
reachable `@ForyJsonProvider`:

```java
import org.apache.fory.json.ForyJson;
import org.apache.fory.json.PropertyNamingStrategy;
import org.apache.fory.json.annotation.ForyJsonProvider;

@ForyJsonProvider
public final class JsonConfigs {
private final ForyJson api =
ForyJson.builder()
.writeNullFields(true)
.withPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
.registerCodec(Money.class, new MoneyCodec())
.build();

public JsonConfigs() {}

public ForyJson api() {
return api;
}
}
```

The provider class must be public and concrete and have a public no-argument constructor. Provider
members are public, non-static, zero-argument instance methods whose exact return type is
`ForyJson`. Inherited superclass methods and public interface default methods are included. A
provider may return multiple configurations, and multiple providers may be reachable. Equivalent
configurations are generated once.

Provider objects exist only while the image is built. Prefer a dedicated configuration class with
instance fields and methods as shown above; no application `native-image.properties` entry is
needed, and the provider package does not need to be exported or opened to Fory. Static provider
methods and fields are not supported.

Only configurations returned by a provider receive generated codecs. The default configuration is
not generated implicitly. If a codegen-enabled runtime configuration was not included, Fory JSON
uses its prepared interpreted codecs and logs one process-wide warning recommending a reachable
`@ForyJsonProvider`. `withCodegen(false)` explicitly selects interpreted codecs and does not request
generated-codec lookup. Asynchronous compilation is disabled in a native executable.

Use Fory JSON Mixins for models that cannot be modified:

```java
import org.apache.fory.json.ForyJson;
Expand All @@ -105,40 +140,25 @@ public class JsonExample {

`JsonMixin` is a build-time entry point for its exact declared target, so the target does not need
`JsonType` solely to use the Mixin. The registered Mixin class literal must be reachable from the
application. The processor emits available target operations for each non-empty Mixin, and the
Fory JSON Native Image Feature retains the effective runtime metadata. Normal runtime codec
precedence still selects the representation. An empty Mixin produces no generated output.
application. The Native Image Feature retains the target metadata and prepares the same access as
it does for a direct `JsonType` model. A provider configuration generates the Mixin
target only when that exact Mixin is registered in the returned `ForyJson`.

Only one source is enabled for an exact target in a built `ForyJson`. Later registration replaces
an earlier source for subsequent `build()` calls; a runtime keeps the immutable snapshot it was
built with. If the target also has a direct `JsonType` companion, a non-empty registered Mixin
selects the pair-specific artifact instead of combining the overlay with the direct companion.

Do not add application reflection configuration as a replacement for the generated configuration.
The native executable resolves the same effective annotations as the JVM.

The processor generates direct property and creator operations. The `fory-json` artifact activates
its Native Image Feature automatically and retains the generated factories and required model
metadata. `@JsonType` is not inherited, so annotate every concrete runtime model. An annotated base
with a class-literal `@JsonSubTypes` table registers those listed subtypes automatically, but each
concrete object subtype needs its own direct `@JsonType` to receive generated operations. Reachable
concrete `Collection` and `Map` root types are also supported when they
have the public no-argument constructor required by Fory JSON. Reachable `@JsonCodec` declarations
register their codec constructor even when the declaration target is not an object model. A class
referenced only by a runtime string is not reachable; `JsonSubTypes.Type.className` is therefore
unsupported in a native image.

Native execution uses Fory JSON's interpreted readers and writers with the generated property and
creator operations. `ForyJson.builder()` automatically
disables runtime code generation and asynchronous compilation in the native executable, while all
other builder options retain their normal behavior. Applications can create differently configured
`ForyJson` instances at runtime and do not need build-time initialization or reflection
configuration.
built with.

The `fory-json` artifact activates its Native Image Feature automatically. `@JsonType` is not
inherited, so annotate every concrete runtime model. An annotated base with a class-literal
`@JsonSubTypes` table registers its listed subtypes automatically. Reachable concrete `Collection`
and `Map` root types are supported when they have the public no-argument constructor required by
Fory JSON. A class referenced only by a runtime string is not reachable;
`JsonSubTypes.Type.className` is therefore unsupported in a native image.

Type, field, effective ordinary getter, setter value parameter, and `JsonCreator` parameter
`@JsonCodec` annotations are supported. The Feature registers every selected complete-value,
element, content, Map-key, and Map-value codec constructor. This is the same annotation model used
on the JVM and Android.
`@JsonCodec` annotations are supported. The Feature retains every selected complete-value, element,
content, Map-key, and Map-value codec constructor. This is the same annotation model used on the
JVM and Android.

`JsonValue` fields and effective public zero-argument methods are supported, including matching
one-String `JsonCreator` constructors and public static factories. Fixed `JsonRawValue` fields and
Expand All @@ -154,21 +174,20 @@ instead.
`@JsonCodec(valueCodec = ...)` on that field or getter to customize each dynamic value. A second
`JsonAnySetter` parameter may use the normal configuration for its own value shape.

`JsonUnwrapped` uses the same interpreted behavior as on the JVM. For direct target annotations,
annotate the containing model and every unwrapped child or intermediate object with `JsonType` so
each model receives its generated property and creator operations. A Mixin retains the
unwrapped models reached by its effective schema; register a separate exact Mixin for a child only
when that child's annotations also need an overlay.
`JsonUnwrapped` uses the same behavior as on the JVM. For direct target annotations, annotate the
containing model and every unwrapped child or intermediate object with `JsonType`. A Mixin retains
the unwrapped models reached by its effective schema; register a separate exact Mixin for a child
only when that child's annotations also need an overlay.

Child codecs act on one direct level. `elementCodec` supports `Collection`, Java arrays, and
`AtomicReferenceArray`; `contentCodec` supports `Optional` and `AtomicReference`; `keyCodec` and
`valueCodec` support Map keys and values. A complete `value` codec cannot be combined with a child
codec.

An annotation codec must have the same public no-argument constructor required on the JVM. In a
named module, export or open its package to `org.apache.fory.json`. A codec instance supplied
through `registerCodec` is constructed by the application and needs no annotation-constructor
metadata.
An annotation codec must have a public no-argument constructor. Fory prepares that constructor
during Native Image construction, so application modules do not need to export or open the codec
package. A codec instance supplied through `registerCodec` is constructed by the application and
needs no annotation-constructor metadata.

## Basic Usage

Expand Down
36 changes: 21 additions & 15 deletions docs/guide/java/json-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,10 @@ independently to each reader; zero disables the cache, and the setting does not
input. The buffer setting does not limit output size. Builder changes after `build()` do not mutate
an existing runtime.

In a GraalVM native image, runtime code generation and asynchronous compilation are automatically
disabled. Every other builder option keeps the behavior described above.
In a GraalVM native image, runtime compilation and asynchronous compilation are unavailable.
Configurations returned by a reachable `ForyJsonProvider` use codecs generated while the image is
built; other configurations use interpreted codecs with build-time-prepared access metadata. Every
other builder option keeps the behavior described above.

## Annotations

Expand Down Expand Up @@ -335,15 +337,17 @@ import org.apache.fory.json.annotation.JsonUnwrapped;
```

`JsonType` asks the annotation processor to generate direct property and creator operations plus
the exact retention rules for an eligible concrete object model. A directly annotated
`JsonValue` Record also receives a companion so its value accessor and canonical constructor work
after Android desugaring. The same generated companion is used on the JVM, Android, and GraalVM
Native Image. The annotation is not inherited; a concrete subtype needs its own direct annotation
to receive a companion. See
[GraalVM Support](graalvm-support.md) and [Android Support](android-support.md) for setup.
A directly annotated model that uses the default object codec requires that generated companion;
the exact retention rules for an eligible concrete object model on the JVM and Android. A directly
annotated `JsonValue` Record also receives a companion so its value accessor and canonical
constructor work after Android desugaring. The annotation is not inherited; a concrete subtype
needs its own direct annotation to receive a companion on those runtimes. A directly annotated
model that uses the default object codec requires that generated companion outside Native Image;
the runtime reports a configuration error if the processor output is missing.

GraalVM Native Image discovers `JsonType` directly and does not use annotation-processor output.
See [GraalVM Support](graalvm-support.md) for optional provider-based hosted code generation and
[Android Support](android-support.md) for annotation-processor setup.

### Mixins

Use a Mixin to configure an existing class without changing its source:
Expand Down Expand Up @@ -427,9 +431,10 @@ Mixin does not introduce a separate record-component model. Use source selectors
declarations and keep repeated annotations consistent as required by normal record property
mapping.

On Android and GraalVM Native Image, compile non-empty Mixin sources with the Fory annotation
processor so required generated operations and platform configuration are available. See
[Android Support](android-support.md) and [GraalVM Support](graalvm-support.md).
On Android, compile non-empty Mixin sources with the Fory annotation processor so required
generated operations and platform configuration are available. GraalVM Native Image discovers
reachable Mixins directly. See [Android Support](android-support.md) and
[GraalVM Support](graalvm-support.md).

### `JsonProperty`

Expand Down Expand Up @@ -1171,9 +1176,10 @@ no-argument constructor. One instance is shared by all annotated sites and concu
the built `ForyJson`, so it must be thread-safe. Use `registerCodec(Target.class, instance)` when a
complete-value codec needs configuration.

In a named Java module, export or open the codec package to `org.apache.fory.json`. When an inherited
type-declaration codec is used for a more specific target, every decoded value must be null or
assignable to that target.
Outside GraalVM Native Image, a named Java module must export or open the codec package to
`org.apache.fory.json`. Native Image prepares annotation-codec constructors during image
construction and does not require that package access. When an inherited type-declaration codec is
used for a more specific target, every decoded value must be null or assignable to that target.

The annotation has the same FIELD, METHOD, and PARAMETER behavior on the JVM, Android, and GraalVM
Native Image. Ordinary Android classes may omit `JsonType` and provide equivalent exact rules.
Expand Down
14 changes: 7 additions & 7 deletions integration_tests/graalvm_tests/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# GraalVM Native Image Tests

Examples and tests for Fory serialization in GraalVM Native Image. The Fory JSON
entry point covers direct `JsonType` models and runtime registration of exact
`JsonMixin` target/source mappings. Native-image hosted analysis resolves each mapping and registers
its generated factory when present. The built executables then execute direct and Mixin mappings at
runtime; the module-path run verifies the same factories through JPMS.
Examples and tests for Fory serialization in GraalVM Native Image. The Fory JSON entry point is
compiled with annotation processing disabled. It covers direct `JsonType` models, exact
`JsonMixin` target/source mappings, provider-selected hosted codec generation, configuration
fallback to interpreted codecs, and hosted access metadata for unprovided configurations in one
native image.

## Test

```bash
mvn clean -DskipTests=true -Pnative package
mvn -DmainClass=org.apache.fory.graalvm.ForyJsonExample clean -DskipTests=true -Dexec.skip=true -Pnative package
./target/main
mvn clean -DskipTests=true -Pnative-module package
mvn -DmainClass=org.apache.fory.graalvm.ForyJsonExample clean -DskipTests=true -Pnative-module package
./target/main-module
```

Expand Down
8 changes: 1 addition & 7 deletions integration_tests/graalvm_tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,7 @@
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.source}</target>
<annotationProcessorPaths>
<path>
<groupId>org.apache.fory</groupId>
<artifactId>fory-annotation-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<proc>none</proc>
</configuration>
</plugin>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
requires org.apache.fory.json;
requires java.sql;

// Fory-generated codecs and annotation codecs access the test models from library modules.
// Binary serialization acceptance retains its existing exported and opened model packages.
// The Fory JSON closed-package test intentionally uses neither directive.
exports org.apache.fory.graalvm;
exports org.apache.fory.graalvm.record;

Expand Down
Loading
Loading