This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit 675325d7306e27be30d089bb09a07760801abc6b Author: chaokunyang <[email protected]> AuthorDate: Wed May 13 04:18:07 2026 +0000 🔄 synced local 'docs/guide/' with remote 'docs/guide/' --- docs/guide/java/android-support.md | 184 +++++++++++------------- docs/guide/java/configuration.md | 3 +- docs/guide/java/index.md | 1 + docs/guide/java/schema-evolution.md | 16 ++- docs/guide/java/static-generated-serializers.md | 138 ++++++++++++++++++ 5 files changed, 236 insertions(+), 106 deletions(-) diff --git a/docs/guide/java/android-support.md b/docs/guide/java/android-support.md index c8f316313a..c3b68088ef 100644 --- a/docs/guide/java/android-support.md +++ b/docs/guide/java/android-support.md @@ -19,136 +19,116 @@ license: | limitations under the License. --- -## Android Support +## Android Runtime -This page documents the Java `fory-core` Android runtime surface. +Fory Java supports Android 8.0+ (API level 26+) through the regular `fory-core` artifact. No separate +Android artifact is required for core object serialization. -The target runtime is Android 8.0+ (API level 26+) in the existing `fory-core` artifact. Android -support is selected at runtime by `org.apache.fory.platform.AndroidSupport`; no separate Android -artifact is required for core object serialization. +Use core object serialization on Android: -`java/fory-format` is not part of the Android support surface. Row-format direct-memory APIs remain -JVM-only. - -Android does not allow Fory runtime serialization paths to rely on `sun.misc.Unsafe`, private-field -`MethodHandle` access, dynamic bytecode loading, or `LambdaMetafactory`. Android-specific code paths -must use public platform APIs or fail with targeted exceptions when Android cannot preserve JVM -semantics. - -## Target Support Surface +- `Fory#serialize(Object)` and `Fory#deserialize(byte[])`. +- `BaseFory#deserialize(ByteBuffer)` for heap, direct, and read-only `ByteBuffer` inputs. +- Stream, channel, and out-of-band buffer APIs through byte-array, heap-buffer, or `ByteBuffer` copy + paths. +- Java collections/maps and xlang collections/maps. -The Android target includes: +`java/fory-format` row-format APIs are JVM-only and are not supported on Android. -- `Fory#serialize(Object)` returning `byte[]`. -- `Fory#deserialize(byte[])`. -- `BaseFory#deserialize(ByteBuffer)` through copy into a Fory-owned heap `MemoryBuffer`. -- Stream, channel, and out-of-band buffer APIs through safe heap, byte-array, or `ByteBuffer` copy - paths. -- Interpreter object serializers with reflection-backed field access. -- Normal Java collections/maps and xlang collection/map protocols. +## Runtime Codegen -Unsupported or removed behavior: +Runtime serializer code generation is disabled on Android. If `withCodegen(true)` is set, Fory keeps +Android serialization on the non-codegen path and logs a warning. -- Runtime serializer code generation and async compilation. -- Lambda and `SerializedLambda` serialization. Registration still succeeds for stable internal type - ids, but write/read/copy operations throw an unsupported exception. -- Native-address serialize/deserialize APIs and native-address `MemoryBuffer` wrapping. -- Raw-address direct `ByteBuffer` zero-copy. -- Raw unsafe `MemoryBuffer` copy APIs remain JVM-only and throw on Android. -- `java/fory-format` row-format APIs, including direct-memory binary row copy paths. -- Field type-use annotation metadata that depends on `Field#getAnnotatedType()` on the JVM. - Android API 26 field metadata uses generic field types and field annotations exposed by Android - reflection. -- Object deserialization that cannot be completed through reflection. - -## Codegen - -`ForyBuilder` stores the codegen request as a nullable `Boolean`. Unset codegen defaults to disabled -on Android and GraalVM native image, and enabled on ordinary JVM. Explicit `withCodegen(true)` on -Android or GraalVM does not throw; build finalization forces codegen off and emits an explicit -warning. Explicit `withCodegen(false)` and platform-default disabled codegen do not warn. - -Android runtime codegen entry points must fail before Janino, class definition, generated accessor -definition, or generated serializer loading starts. +Android apps that need generated serializers should use build-time static generated serializers +instead. -## ByteBuffer +## Static Generated Serializers -On Android, `BaseFory#deserialize(ByteBuffer)` copies the remaining input bytes into a Fory-owned -heap `MemoryBuffer`, then deserializes from that buffer. Heap, direct, and readonly inputs are -supported through the copy path. The caller buffer position and limit are not changed. +Use `@ForyStruct` static generated serializers for Android application classes. They are generated by +javac during the app build and work without runtime bytecode generation. -Raw direct-buffer address wrapping remains a JVM-only fast path and is not used on Android. +### Install The Annotation Processor -## Direct Memory And Row Format +Add `fory-annotation-processor` to the annotation processor path of the module that compiles your +Android model classes: -Android `fory-core` paths do not execute `sun.misc.Unsafe` operations. Direct-memory copy APIs are -JVM-only paths for existing JVM users such as `java/fory-format`; they throw before unsafe execution -when Android is detected. +```xml +<build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>org.apache.fory</groupId> + <artifactId>fory-annotation-processor</artifactId> + <version>${fory.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + </plugins> +</build> +``` -Use core object serialization on Android. Do not use `java/fory-format` row-format APIs on Android. +Then annotate Android model classes with `@ForyStruct`. -## JDK Collection And Map Wrappers +Static generated serializers are required on Android when a serialized class uses Fory type-use +annotations, for example: -In native Java mode, Android does not add a new wrapper protocol branch and does not rewrite normal -collection/map serializers globally. +```java +import java.util.List; +import org.apache.fory.annotation.ForyStruct; +import org.apache.fory.annotation.UInt8Type; -For `UnmodifiableSerializers` and `SynchronizedSerializers`, Android keeps the outer wrapper -serializer and writes a public source collection/map payload in the existing backing-value slot: +@ForyStruct +public class ImageBlock { + public List<@UInt8Type Integer> pixels; +} +``` -- list wrappers use `ArrayList` source type info. -- set wrappers use `HashSet` source type info. -- sorted or navigable set wrappers use `TreeSet` source type info. -- map wrappers use `HashMap` source type info. -- sorted or navigable map wrappers use `TreeMap` source type info. +Without the generated static descriptors, Android reflection may not expose the nested type-use +metadata needed for annotations such as `@Ref`, `@Int8Type`, `@UInt8Type`, `@Float16Type`, or +`@BFloat16Type`. Serialization for those classes will not have the schema information Fory needs. -The wrapper read path rewraps that source through `Collections.unmodifiable*` or -`Collections.synchronized*`. Synchronized wrapper write and copy paths must hold the wrapper lock -while iterating public contents. +See [Static Generated Serializers](static-generated-serializers.md) for setup instructions. -Sublist views keep a unified serializer protocol. Android writes visible elements; JVM may write -source-list view metadata when supported. Both Android and JVM readers accept both payload modes. +## Object Model Requirements -Other JDK collection serializers keep their existing Java native protocol shape while avoiding -hidden-field unsafe access on Android. `Arrays.asList` writes the existing array payload, -`Collections.newSetFromMap` writes a `HashMap` backing-map payload, bounded blocking queues derive -capacity through public APIs, non-empty `EnumMap` derives its key type from the first key, empty -Android `EnumMap` writes a self-describing Java-serialization fallback, and immutable JDK -collections are materialized through public unmodifiable containers on Android. +Android serializers use public Android runtime capabilities. For application classes, prefer: -In xlang mode, collection and map serialization uses the xlang collection/map protocol and does not -encode Java wrapper/view internals. +- accessible no-argument constructors, or records with supported constructors. +- public, protected, or package-private serialized fields. +- non-private getters and setters for private serialized fields. +- `@ForyStruct` static generated serializers for Android model classes. -## JDK Dynamic Proxies +Final fields in ordinary classes are not suitable for generated read/copy methods. Use records for +constructor-based immutable values. -The Android design supports `java.lang.reflect.Proxy` serialization. +## Unsupported Features -The Android proxy path must use only public proxy APIs: +The following JVM features are not supported on Android: -- `Proxy.getInvocationHandler(proxy)` to read the handler during write and copy. -- `Proxy.newProxyInstance(classLoader, interfaces, handler)` to construct proxies during read and - copy. -- Normal Fory reference serialization for the proxy interface array and invocation handler. +- Runtime serializer code generation and async compilation. +- Lambda and `SerializedLambda` serialization. +- Native-address serialization APIs and native-address `MemoryBuffer` wrapping. +- Raw unsafe memory copy APIs. +- `java/fory-format` row-format APIs. -Android must not read or write the private `Proxy.h` field, request a field offset for that field, -or use `Unsafe` to replace the handler after proxy construction. +## ByteBuffer -For non-cyclic proxies, read constructs the proxy directly with the deserialized invocation handler. -For cyclic proxy graphs with reference tracking, Android uses a private deferred invocation handler: +`BaseFory#deserialize(ByteBuffer)` supports heap, direct, and read-only buffers on Android by copying +the remaining bytes into a Fory-owned heap buffer. The caller buffer position and limit are not +changed. -1. Create the proxy with a deferred handler. -2. Register the proxy in the read or copy reference table before reading or copying the real handler. -3. Read or copy the real handler through the normal Fory object path. -4. Install the real handler into the deferred handler. +Raw direct-buffer address wrapping is a JVM-only fast path and is not used on Android. -This preserves cycles where an invocation handler references its own proxy without mutating private -JDK fields. The deferred handler is an internal implementation detail and must never be written as -the user handler. When writing or copying a proxy, `JdkProxySerializer` unwraps any deferred handler -before serializing or copying the handler. +## Collections, Maps, And Proxies -A proxy must not be invoked, logged, or used as a key whose hash or equality calls the handler while -the deferred handler is still unresolved during deserialization or copy. If that happens, Fory throws -a targeted exception instead of silently invoking an incomplete proxy. +Common JDK collection and map implementations are supported on Android. In xlang mode, collection and +map serialization uses the xlang protocol and does not encode Java wrapper/view internals. -The JVM path may keep the existing optimized private-handler replacement path when benchmarks -require it. The Android path must remain separate and must not resolve JVM-only proxy handler offset -state. +`java.lang.reflect.Proxy` serialization is supported for normal proxy usage. Do not invoke, log, or +use a proxy as a map/set key while it is still being deserialized; the invocation handler may not be +ready yet. diff --git a/docs/guide/java/configuration.md b/docs/guide/java/configuration.md index 1a0342aabb..e7c09b123e 100644 --- a/docs/guide/java/configuration.md +++ b/docs/guide/java/configuration.md @@ -43,7 +43,7 @@ This page documents all configuration options available through `ForyBuilder`. | `scopedMetaShareEnabled` | Scoped meta share focuses on a single serialization process. Metadata created or identified during this process is exclusive to it and is not shared with by other serializations. [...] | `metaCompressor` | Set a compressor for meta compression. Note that the passed MetaCompressor should be thread-safe. By default, a `Deflater` based compressor `DeflaterMetaCompressor` will be used. Users can pass other compressor such as `zstd` for better compression rate. [...] | `deserializeUnknownClass` | Enables or disables deserialization/skipping of data for non-existent or unknown classes. [...] -| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. When unset, codegen defaults to enabled on ordinary JVMs and disabled on Android and GraalVM native image. Explicit `withCodegen(true)` on Android or GraalVM native image is accepted, but final build configuration forces interpreter serializers and emits a warning. [...] +| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. When unset, codegen defaults to enabled on ordinary JVMs and disabled on Android and GraalVM native image. Explicit `withCodegen(true)` on Android or GraalVM native image is accepted, but final build configuration forces interpreter serializers and emits a warning. If a build-time `@ForyStruct` static serializer is available, ordinary JVM `withCodegen(false)` [...] | `asyncCompilationEnabled` | If enabled, serialization uses interpreter mode first and switches to JIT serialization after async serializer JIT for a class is finished. This option is forced off on Android and GraalVM native image because runtime code generation is unavailable there. [...] | `scalaOptimizationEnabled` | Enables or disables Scala-specific serialization optimization. [...] | `copyRef` | When disabled, the copy performance will be better. But fory deep copy will ignore circular and shared reference. Same reference of an object graph will be copied into different objects in one `Fory#copy`. [...] @@ -77,3 +77,4 @@ Fory fory = Fory.builder() - [Schema Evolution](schema-evolution.md) - Compatible mode and meta sharing - [Compression](compression.md) - Int, long, and array compression details - [Type Registration](type-registration.md) - Class registration options +- [Static Generated Serializers](static-generated-serializers.md) - Annotation-processor static generated serializers for `@ForyStruct`, `codegen=false`, and Android diff --git a/docs/guide/java/index.md b/docs/guide/java/index.md index 59cca958fc..3e4d29d493 100644 --- a/docs/guide/java/index.md +++ b/docs/guide/java/index.md @@ -210,4 +210,5 @@ ThreadSafeFory threadLocalFory = Fory.builder() - [Type Registration](type-registration.md) - Class registration and security - [Custom Serializers](custom-serializers.md) - Implement custom serializers - [Cross-Language Serialization](cross-language.md) - Serialize data for other languages +- [Static Generated Serializers](static-generated-serializers.md) - Annotation-processor static generated serializers for `@ForyStruct` - [GraalVM Support](graalvm-support.md) - Build-time serializer compilation for native images diff --git a/docs/guide/java/schema-evolution.md b/docs/guide/java/schema-evolution.md index d2db111388..b99a3c5465 100644 --- a/docs/guide/java/schema-evolution.md +++ b/docs/guide/java/schema-evolution.md @@ -48,14 +48,24 @@ System.out.println(fory.deserialize(bytes)); This compatible mode involves serializing class metadata into the serialized output. Despite Fory's use of sophisticated compression techniques to minimize overhead, there is still some additional space cost associated with class metadata. -### Disable Evolution for Stable Classes +### Per-Class Evolution Policy -If a class schema is stable and will not change, you can opt out of schema evolution on a per-class basis to avoid compatible metadata overhead. Annotate the class with `@ForyStruct(evolving = false)` to force `STRUCT/NAMED_STRUCT` type IDs even when Compatible mode is enabled. +`@ForyStruct` can set a per-class evolution policy: + +- `Evolution.INHERIT`: follow the Fory instance's compatible/meta-share configuration. This is the default. +- `Evolution.ENABLED`: require schema evolution metadata for this class. Registration or type resolution fails if the Fory instance cannot emit that metadata. +- `Evolution.DISABLED`: force fixed-schema `STRUCT/NAMED_STRUCT` encoding even when compatible metadata is otherwise enabled. + +Use `@ForyStruct(evolution = Evolution.DISABLED)` for fixed-schema structs. The legacy boolean +form `@ForyStruct(evolving = false)` is still supported as a fixed-schema opt-out. + +If a class schema is stable and will not change, opt out of schema evolution on that class to avoid compatible metadata overhead: ```java import org.apache.fory.annotation.ForyStruct; +import org.apache.fory.annotation.ForyStruct.Evolution; -@ForyStruct(evolving = false) +@ForyStruct(evolution = Evolution.DISABLED) public class StableMessage { public int id; public String name; diff --git a/docs/guide/java/static-generated-serializers.md b/docs/guide/java/static-generated-serializers.md new file mode 100644 index 0000000000..ec101027c5 --- /dev/null +++ b/docs/guide/java/static-generated-serializers.md @@ -0,0 +1,138 @@ +--- +title: Static Generated Serializers +sidebar_position: 15 +id: static_generated_serializers +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Static generated serializers are Java serializers generated by javac during your application build. +They are useful when runtime code generation is disabled or unavailable. + +Use them when: + +- you run on Android. +- you run an ordinary JVM with `ForyBuilder#withCodegen(false)` and want generated serializers. +- your Android model classes use Fory type-use annotations such as `@Ref`, `@UInt8Type`, or + `@Float16Type`. + +For GraalVM native images, follow [GraalVM Support](graalvm-support.md) instead. + +## Install The Annotation Processor + +Add `fory-annotation-processor` to the annotation processor path of the module that compiles your +serializable classes: + +```xml +<build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>org.apache.fory</groupId> + <artifactId>fory-annotation-processor</artifactId> + <version>${fory.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + </plugins> +</build> +``` + +The generated serializers depend on `fory-core` at runtime. Applications opt in by adding the +annotation processor; `fory-core` does not depend on it. + +## Annotate Classes + +Annotate each serializable class with `@ForyStruct`: + +```java +import org.apache.fory.annotation.ForyStruct; + +@ForyStruct +public class Order { + public long id; + public String note; + + public Order() {} +} +``` + +The processor generates serializer classes in the same Java package as the annotated class. For +`Order`, the generated classes are: + +- `Order__ForySerializer__` for cross-language mode. +- `Order__ForyNativeSerializer__` for Java native mode. + +For a static nested type such as `Outer.Inner`, the generated top-level classes are +`Outer$Inner__ForySerializer__` and `Outer$Inner__ForyNativeSerializer__`. + +## Runtime Use + +Fory uses static generated serializers when they are available on: + +- Android. +- ordinary JVMs with `ForyBuilder#withCodegen(false)`. +- compatible-mode reads when the target struct has a generated serializer. + +On an ordinary JVM with `codegen=true`, Fory continues to prefer runtime-generated serializers. + +## Field Access Rules + +Generated serializers must be able to access serialized fields or their accessors at compile time. + +- Public, protected, and package-private fields can be accessed directly when Java package access + allows same-package generated serializers to use them. +- Private serialized fields must have accessible non-private getter and setter methods, or be + excluded with `transient` or Fory `@Ignore`. +- Public, protected, and package-private getter/setter methods are accepted when they are accessible + from the generated serializer package. +- Final fields are not supported for normal mutable classes because generated read and copy methods + must assign fields. Use records for constructor-based immutable values. + +For records, generated serializers use public record accessors and construct values through the +canonical record constructor. Ignored record components are skipped by serialization and copy, and +their constructor arguments use Java default values during generated read/copy. + +## Type-Use Annotations On Android + +On Android, static generated serializers are required when a class uses Fory type-use annotations on +nested types: + +```java +import java.util.List; +import org.apache.fory.annotation.ForyStruct; +import org.apache.fory.annotation.UInt8Type; + +@ForyStruct +public class ImageBlock { + public List<@UInt8Type Integer> pixels; +} +``` + +Without the generated serializer metadata, Android may not expose enough nested type information for +Fory to preserve annotations such as `@Ref`, `@Int8Type`, `@UInt8Type`, `@Float16Type`, or +`@BFloat16Type`. + +## Compatible Reads + +Static generated serializers support both normal serialization and compatible-mode reads. Compatible +reads match remote fields to local fields, skip fields that no longer exist locally, and keep Java +default values for fields that are missing from the remote payload. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
