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.git
The following commit(s) were added to refs/heads/main by this push:
new f5a114b6a feat(java): remove guava dependency from fory java (#3557)
f5a114b6a is described below
commit f5a114b6a6a3fe35bec2c8dd6edcdca7cba93887
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Apr 12 20:31:43 2026 +0800
feat(java): remove guava dependency from fory java (#3557)
## Why?
Make `fory-core` usable without requiring Guava on the runtime classpath
while keeping Guava
collection support available when the dependency is present.
## What does this PR do?
- Marks the `guava` dependency in `java/fory-core` as optional.
- Replaces the remaining Guava-based core utilities with local
`org.apache.fory.collection`
implementations for the cache, map maker, bi-map, weak-reference
cleanup, and related helpers.
- Keeps Guava collection serializers available behind runtime detection
and preserves reserved type IDs
when Guava registration is disabled or Guava is absent.
- Moves Guava collection serializer coverage into `java/fory-testsuite`,
adds explicit test-scope Guava
dependencies there and in `java/fory-format`, and adds
`GuavaOptionalDependencyTest` for the
no-Guava classpath case.
- Removes a few incidental Guava usages from Java benchmarks/tests and
updates native-image
initialization metadata for the new collection classes.
## Related issues
Closes #1113
#1114 #1335
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [x] `fory-core` no longer requires Guava on the runtime classpath;
Guava-specific serializers remain available when Guava is present.
- [x] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
Not run.
---
.../fory/benchmark/state/FlatBuffersState.java | 2 +-
.../fory/benchmark/state/ProtoBuffersState.java | 2 +-
.../org/apache/fory/benchmark/state/JsonTest.java | 24 +-
java/fory-core/pom.xml | 1 +
.../java/org/apache/fory/collection/BiMap.java | 69 +++++
.../java/org/apache/fory/collection/Cache.java | 34 +++
.../org/apache/fory/collection/CacheBuilder.java | 101 ++++++
.../apache/fory/collection/ClassValueCache.java | 2 -
.../org/apache/fory/collection/Collections.java | 3 -
.../java/org/apache/fory/collection/MapMaker.java | 48 +++
.../apache/fory/collection/MultiKeyWeakMap.java | 47 ++-
.../fory/collection/ReferenceConcurrentMap.java | 338 +++++++++++++++++++++
.../java/org/apache/fory/config/ForyBuilder.java | 4 +-
.../org/apache/fory/resolver/ClassResolver.java | 32 +-
.../org/apache/fory/resolver/SharedRegistry.java | 2 +-
.../org/apache/fory/resolver/TypeResolver.java | 6 +-
.../org/apache/fory/serializer/Serializers.java | 4 +-
.../collection/GuavaCollectionSerializers.java | 108 +++++--
.../serializer/collection/MapLikeSerializer.java | 25 --
.../fory/serializer/converter/FieldConverters.java | 21 +-
.../main/java/org/apache/fory/type/Descriptor.java | 4 +-
.../fory-core/native-image.properties | 77 +----
.../apache/fory/GuavaOptionalDependencyTest.java | 146 +++++++++
java/fory-format/pom.xml | 5 +
java/fory-testsuite/pom.xml | 5 +
.../collection/GuavaCollectionSerializersTest.java | 103 +++++--
.../fory/serializer/scala/ScalaDispatcher.java | 2 +-
27 files changed, 1028 insertions(+), 187 deletions(-)
diff --git
a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/FlatBuffersState.java
b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/FlatBuffersState.java
index 6ef412481..492e6521e 100644
---
a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/FlatBuffersState.java
+++
b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/FlatBuffersState.java
@@ -19,7 +19,6 @@
package org.apache.fory.benchmark.state;
-import com.google.common.base.Preconditions;
import com.google.flatbuffers.FlatBufferBuilder;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -40,6 +39,7 @@ import org.apache.fory.benchmark.state.generated.FBSMedia;
import org.apache.fory.benchmark.state.generated.FBSMediaContent;
import org.apache.fory.benchmark.state.generated.FBSSample;
import org.apache.fory.memory.ByteBufferUtil;
+import org.apache.fory.util.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
diff --git
a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ProtoBuffersState.java
b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ProtoBuffersState.java
index 7fc94e1e9..d581c2b30 100644
---
a/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ProtoBuffersState.java
+++
b/benchmarks/java/src/main/java/org/apache/fory/benchmark/state/ProtoBuffersState.java
@@ -19,7 +19,6 @@
package org.apache.fory.benchmark.state;
-import com.google.common.base.Preconditions;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.HashMap;
import java.util.function.Function;
@@ -31,6 +30,7 @@ import org.apache.fory.benchmark.data.Sample;
import org.apache.fory.benchmark.state.Example.Bar;
import org.apache.fory.benchmark.state.Example.Foo;
import org.apache.fory.integration_tests.state.generated.ProtoMessage;
+import org.apache.fory.util.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.Fork;
diff --git
a/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/JsonTest.java
b/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/JsonTest.java
index 48e03ba57..b28f1cae7 100644
---
a/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/JsonTest.java
+++
b/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/JsonTest.java
@@ -20,8 +20,7 @@
package org.apache.fory.benchmark.state;
import com.alibaba.fastjson2.JSONObject;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
+import java.util.ArrayList;
import java.util.List;
import org.apache.fory.Fory;
import org.apache.fory.collection.Collections;
@@ -44,15 +43,18 @@ public class JsonTest {
@DataProvider
public static Object[][] config() {
- return Sets.cartesianProduct(
- ImmutableSet.of(true, false), // referenceTracking
- ImmutableSet.of(true, false), // compatible mode
- ImmutableSet.of(true, false), // scoped meta share mode
- ImmutableSet.of(true, false) // fory enable codegen
- )
- .stream()
- .map(List::toArray)
- .toArray(Object[][]::new);
+ boolean[] options = new boolean[] {true, false};
+ List<Object[]> configs = new ArrayList<>(16);
+ for (boolean trackingRef : options) {
+ for (boolean compatible : options) {
+ for (boolean scoped : options) {
+ for (boolean codegen : options) {
+ configs.add(new Object[] {trackingRef, compatible, scoped,
codegen});
+ }
+ }
+ }
+ }
+ return configs.toArray(new Object[0][]);
}
@Test(dataProvider = "config")
diff --git a/java/fory-core/pom.xml b/java/fory-core/pom.xml
index d6fd1b60c..8163a7130 100644
--- a/java/fory-core/pom.xml
+++ b/java/fory-core/pom.xml
@@ -50,6 +50,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
+ <optional>true</optional>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
diff --git a/java/fory-core/src/main/java/org/apache/fory/collection/BiMap.java
b/java/fory-core/src/main/java/org/apache/fory/collection/BiMap.java
new file mode 100644
index 000000000..e483075fd
--- /dev/null
+++ b/java/fory-core/src/main/java/org/apache/fory/collection/BiMap.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory.collection;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/** Minimal bidirectional map backed by caller-provided forward and reverse
maps. */
+public final class BiMap<K, V> {
+ private final Map<K, V> valuesByKey;
+ private final Map<V, K> keysByValue;
+ private final Inverse inverse = new Inverse();
+
+ public BiMap(Map<K, V> valuesByKey, Map<V, K> keysByValue) {
+ this.valuesByKey = valuesByKey;
+ this.keysByValue = keysByValue;
+ }
+
+ public static <K, V> BiMap<K, V> newHashIdentityBiMap(int expectedSize) {
+ return new BiMap<>(new HashMap<>(expectedSize), new
IdentityHashMap<>(expectedSize));
+ }
+
+ public V get(K key) {
+ return valuesByKey.get(key);
+ }
+
+ public boolean containsKey(K key) {
+ return valuesByKey.containsKey(key);
+ }
+
+ public void put(K key, V value) {
+ valuesByKey.put(key, value);
+ keysByValue.put(value, key);
+ }
+
+ public Inverse inverse() {
+ return inverse;
+ }
+
+ public final class Inverse {
+ private Inverse() {}
+
+ public boolean containsKey(V value) {
+ return keysByValue.containsKey(value);
+ }
+
+ public K get(V value) {
+ return keysByValue.get(value);
+ }
+ }
+}
diff --git a/java/fory-core/src/main/java/org/apache/fory/collection/Cache.java
b/java/fory-core/src/main/java/org/apache/fory/collection/Cache.java
new file mode 100644
index 000000000..c079597ef
--- /dev/null
+++ b/java/fory-core/src/main/java/org/apache/fory/collection/Cache.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory.collection;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+/** Minimal cache API used by fory-core. */
+public interface Cache<K, V> {
+ V getIfPresent(K key);
+
+ V get(K key, Callable<? extends V> loader) throws ExecutionException;
+
+ void put(K key, V value);
+
+ void cleanUp();
+}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/CacheBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/collection/CacheBuilder.java
new file mode 100644
index 000000000..445984821
--- /dev/null
+++ b/java/fory-core/src/main/java/org/apache/fory/collection/CacheBuilder.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory.collection;
+
+import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import org.apache.fory.util.Preconditions;
+
+/** Minimal cache builder supporting the subset used by fory-core. */
+public final class CacheBuilder<K, V> {
+ private boolean weakKeys;
+ private boolean softValues;
+ private int concurrencyLevel = 4;
+
+ private CacheBuilder() {}
+
+ public static CacheBuilder<Object, Object> newBuilder() {
+ return new CacheBuilder<>();
+ }
+
+ public CacheBuilder<K, V> weakKeys() {
+ weakKeys = true;
+ return this;
+ }
+
+ public CacheBuilder<K, V> softValues() {
+ softValues = true;
+ return this;
+ }
+
+ public CacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
+ Preconditions.checkArgument(concurrencyLevel > 0, "concurrencyLevel must
be positive");
+ this.concurrencyLevel = concurrencyLevel;
+ return this;
+ }
+
+ public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
+ return new LocalCache<>(new ReferenceConcurrentMap<>(weakKeys, softValues,
concurrencyLevel));
+ }
+
+ private static final class LocalCache<K, V> implements Cache<K, V> {
+ private final ReferenceConcurrentMap<K, V> cache;
+
+ private LocalCache(ReferenceConcurrentMap<K, V> cache) {
+ this.cache = cache;
+ }
+
+ @Override
+ public V getIfPresent(K key) {
+ return cache.get(key);
+ }
+
+ @Override
+ public V get(K key, Callable<? extends V> loader) throws
ExecutionException {
+ Objects.requireNonNull(loader);
+ V value = cache.get(key);
+ if (value != null) {
+ return value;
+ }
+ V loadedValue;
+ try {
+ loadedValue = loader.call();
+ } catch (Exception e) {
+ throw new ExecutionException(e);
+ }
+ if (loadedValue == null) {
+ return null;
+ }
+ V existing = cache.putIfAbsent(key, loadedValue);
+ return existing != null ? existing : loadedValue;
+ }
+
+ @Override
+ public void put(K key, V value) {
+ cache.put(key, value);
+ }
+
+ @Override
+ public void cleanUp() {
+ cache.cleanUp();
+ }
+ }
+}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/ClassValueCache.java
b/java/fory-core/src/main/java/org/apache/fory/collection/ClassValueCache.java
index b511fd27c..7b16fce0d 100644
---
a/java/fory-core/src/main/java/org/apache/fory/collection/ClassValueCache.java
+++
b/java/fory-core/src/main/java/org/apache/fory/collection/ClassValueCache.java
@@ -19,8 +19,6 @@
package org.apache.fory.collection;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.apache.fory.annotation.Internal;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/Collections.java
b/java/fory-core/src/main/java/org/apache/fory/collection/Collections.java
index a3f0232c6..2d3d95655 100644
--- a/java/fory-core/src/main/java/org/apache/fory/collection/Collections.java
+++ b/java/fory-core/src/main/java/org/apache/fory/collection/Collections.java
@@ -19,9 +19,6 @@
package org.apache.fory.collection;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.MapMaker;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/MapMaker.java
b/java/fory-core/src/main/java/org/apache/fory/collection/MapMaker.java
new file mode 100644
index 000000000..eacb1029b
--- /dev/null
+++ b/java/fory-core/src/main/java/org/apache/fory/collection/MapMaker.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory.collection;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.fory.util.Preconditions;
+
+/** Minimal map builder supporting the subset used by fory-core. */
+public final class MapMaker {
+ private boolean weakKeys;
+ private int concurrencyLevel = 4;
+
+ public MapMaker weakKeys() {
+ weakKeys = true;
+ return this;
+ }
+
+ public MapMaker concurrencyLevel(int concurrencyLevel) {
+ Preconditions.checkArgument(concurrencyLevel > 0, "concurrencyLevel must
be positive");
+ this.concurrencyLevel = concurrencyLevel;
+ return this;
+ }
+
+ public <K, V> Map<K, V> makeMap() {
+ if (weakKeys) {
+ return new ReferenceConcurrentMap<>(true, false, concurrencyLevel);
+ }
+ return new ConcurrentHashMap<>(Math.max(16, concurrencyLevel));
+ }
+}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java
b/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java
index 096497293..4efb254a4 100644
---
a/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java
+++
b/java/fory-core/src/main/java/org/apache/fory/collection/MultiKeyWeakMap.java
@@ -19,8 +19,8 @@
package org.apache.fory.collection;
-import com.google.common.base.FinalizableReferenceQueue;
-import com.google.common.base.FinalizableWeakReference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.fory.util.GraalvmSupport;
@@ -42,7 +43,8 @@ import org.apache.fory.util.GraalvmSupport;
*/
public class MultiKeyWeakMap<T> {
private static final Set<KeyReference> REFERENCES =
ConcurrentHashMap.newKeySet();
- private static volatile FinalizableReferenceQueue referenceQueue;
+ private static final AtomicBoolean CLEANER_STARTED = new
AtomicBoolean(false);
+ private static volatile ReferenceQueue<Object> referenceQueue;
private final Map<Object, T> map;
public MultiKeyWeakMap() {
@@ -76,22 +78,49 @@ public class MultiKeyWeakMap<T> {
return keyRefs;
}
- private static FinalizableReferenceQueue getReferenceQueue() {
- FinalizableReferenceQueue queue = referenceQueue;
+ private static ReferenceQueue<Object> getReferenceQueue() {
+ ReferenceQueue<Object> queue = referenceQueue;
if (queue == null) {
synchronized (MultiKeyWeakMap.class) {
queue = referenceQueue;
if (queue == null) {
- queue = new FinalizableReferenceQueue();
+ queue = new ReferenceQueue<>();
referenceQueue = queue;
+ startCleaner(queue);
}
}
}
return queue;
}
+ private static void startCleaner(ReferenceQueue<Object> queue) {
+ if (!CLEANER_STARTED.compareAndSet(false, true)) {
+ return;
+ }
+ Thread cleaner =
+ new Thread(
+ () -> {
+ while (true) {
+ try {
+ CleanupReference reference = (CleanupReference)
queue.remove();
+ reference.cleanup();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ },
+ "fory-multi-key-weak-map-cleaner");
+ cleaner.setDaemon(true);
+ cleaner.start();
+ }
+
private interface KeyReference {}
+ private interface CleanupReference {
+ void cleanup();
+ }
+
private static final class NoCallbackRef implements KeyReference {
private final Object obj;
@@ -117,8 +146,8 @@ public class MultiKeyWeakMap<T> {
}
}
- private final class FinalizableKeyReference extends
FinalizableWeakReference<Object>
- implements KeyReference {
+ private final class FinalizableKeyReference extends WeakReference<Object>
+ implements KeyReference, CleanupReference {
private final boolean[] reclaimedFlags;
private final int index;
private final List<FinalizableKeyReference> keyRefs;
@@ -135,7 +164,7 @@ public class MultiKeyWeakMap<T> {
}
@Override
- public void finalizeReferent() {
+ public void cleanup() {
reclaimedFlags[index] = true;
REFERENCES.remove(this);
if (IntStream.range(0, reclaimedFlags.length).allMatch(i ->
reclaimedFlags[i])) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/collection/ReferenceConcurrentMap.java
b/java/fory-core/src/main/java/org/apache/fory/collection/ReferenceConcurrentMap.java
new file mode 100644
index 000000000..8d40b4ed7
--- /dev/null
+++
b/java/fory-core/src/main/java/org/apache/fory/collection/ReferenceConcurrentMap.java
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory.collection;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Concurrent map implementation supporting weak keys and optional soft values.
+ *
+ * <p>The implementation only covers the behaviors needed by fory-core.
+ */
+final class ReferenceConcurrentMap<K, V> extends AbstractMap<K, V> implements
ConcurrentMap<K, V> {
+ private final boolean weakKeys;
+ private final boolean softValues;
+ private final ConcurrentHashMap<Object, Object> map;
+ private final ReferenceQueue<K> keyQueue;
+ private final ReferenceQueue<V> valueQueue;
+
+ ReferenceConcurrentMap(boolean weakKeys, boolean softValues, int
concurrencyLevel) {
+ this.weakKeys = weakKeys;
+ this.softValues = softValues;
+ map = new ConcurrentHashMap<>(Math.max(16, concurrencyLevel));
+ keyQueue = weakKeys ? new ReferenceQueue<>() : null;
+ valueQueue = softValues ? new ReferenceQueue<>() : null;
+ }
+
+ void cleanUp() {
+ drainQueues();
+ }
+
+ @Override
+ public V get(Object key) {
+ drainQueues();
+ return dereferenceValue(map.get(lookupKey(key)));
+ }
+
+ @Override
+ public V put(K key, V value) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+ drainQueues();
+ Object mapKey = storedKey(key);
+ return dereferenceValue(map.put(mapKey, storedValue(value, mapKey)));
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+ while (true) {
+ drainQueues();
+ Object lookupKey = lookupKey(key);
+ Object current = map.get(lookupKey);
+ V currentValue = dereferenceValue(current);
+ if (currentValue != null) {
+ return currentValue;
+ }
+ Object mapKey = storedKey(key);
+ Object newValue = storedValue(value, mapKey);
+ if (current == null) {
+ Object existing = map.putIfAbsent(mapKey, newValue);
+ if (existing == null) {
+ return null;
+ }
+ current = existing;
+ } else if (map.replace(lookupKey, current, newValue)) {
+ return null;
+ }
+ }
+ }
+
+ @Override
+ public V remove(Object key) {
+ drainQueues();
+ return dereferenceValue(map.remove(lookupKey(key)));
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ drainQueues();
+ Object lookupKey = lookupKey(key);
+ Object current = map.get(lookupKey);
+ if (current == null) {
+ return false;
+ }
+ V currentValue = dereferenceValue(current);
+ return currentValue != null
+ && Objects.equals(currentValue, value)
+ && map.remove(lookupKey, current);
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(value);
+ while (true) {
+ drainQueues();
+ Object lookupKey = lookupKey(key);
+ Object current = map.get(lookupKey);
+ if (current == null) {
+ return null;
+ }
+ V currentValue = dereferenceValue(current);
+ if (currentValue == null) {
+ continue;
+ }
+ Object mapKey = storedKey(key);
+ if (map.replace(lookupKey, current, storedValue(value, mapKey))) {
+ return currentValue;
+ }
+ }
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ Objects.requireNonNull(key);
+ Objects.requireNonNull(oldValue);
+ Objects.requireNonNull(newValue);
+ while (true) {
+ drainQueues();
+ Object lookupKey = lookupKey(key);
+ Object current = map.get(lookupKey);
+ if (current == null) {
+ return false;
+ }
+ V currentValue = dereferenceValue(current);
+ if (currentValue == null) {
+ continue;
+ }
+ if (!Objects.equals(currentValue, oldValue)) {
+ return false;
+ }
+ Object mapKey = storedKey(key);
+ if (map.replace(lookupKey, current, storedValue(newValue, mapKey))) {
+ return true;
+ }
+ }
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return get(key) != null;
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ cleanUp();
+ }
+
+ @Override
+ public int size() {
+ drainQueues();
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ drainQueues();
+ return map.isEmpty();
+ }
+
+ @Override
+ public Set<Map.Entry<K, V>> entrySet() {
+ drainQueues();
+ Set<Map.Entry<K, V>> entries = new HashSet<>();
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ K key = dereferenceKey(entry.getKey());
+ V value = dereferenceValue(entry.getValue());
+ if (key != null && value != null) {
+ entries.add(new SimpleImmutableEntry<>(key, value));
+ }
+ }
+ return new EntrySet(entries);
+ }
+
+ private void drainQueues() {
+ if (weakKeys) {
+ WeakKeyReference<K> ref;
+ while ((ref = (WeakKeyReference<K>) keyQueue.poll()) != null) {
+ map.remove(ref);
+ }
+ }
+ if (softValues) {
+ SoftValueReference<V> ref;
+ while ((ref = (SoftValueReference<V>) valueQueue.poll()) != null) {
+ map.remove(ref.mapKey, ref);
+ }
+ }
+ }
+
+ private Object lookupKey(Object key) {
+ Objects.requireNonNull(key);
+ return weakKeys ? new LookupKey<>(key) : key;
+ }
+
+ private Object storedKey(K key) {
+ return weakKeys ? new WeakKeyReference<>(key, keyQueue) : key;
+ }
+
+ private Object storedValue(V value, Object mapKey) {
+ return softValues ? new SoftValueReference<>(value, mapKey, valueQueue) :
value;
+ }
+
+ @SuppressWarnings("unchecked")
+ private K dereferenceKey(Object mapKey) {
+ if (!weakKeys) {
+ return (K) mapKey;
+ }
+ return ((WeakKeyReference<K>) mapKey).get();
+ }
+
+ @SuppressWarnings("unchecked")
+ private V dereferenceValue(Object storedValue) {
+ if (storedValue == null) {
+ return null;
+ }
+ if (!softValues) {
+ return (V) storedValue;
+ }
+ SoftValueReference<V> ref = (SoftValueReference<V>) storedValue;
+ V value = ref.get();
+ if (value == null) {
+ map.remove(ref.mapKey, ref);
+ }
+ return value;
+ }
+
+ private static final class EntrySet<K, V> extends AbstractSet<Map.Entry<K,
V>> {
+ private final Set<Map.Entry<K, V>> entries;
+
+ private EntrySet(Set<Map.Entry<K, V>> entries) {
+ this.entries = entries;
+ }
+
+ @Override
+ public Iterator<Map.Entry<K, V>> iterator() {
+ return entries.iterator();
+ }
+
+ @Override
+ public int size() {
+ return entries.size();
+ }
+ }
+
+ private static final class LookupKey<K> {
+ private final K key;
+ private final int hashCode;
+
+ private LookupKey(K key) {
+ this.key = key;
+ hashCode = key.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof LookupKey) {
+ return key.equals(((LookupKey<?>) other).key);
+ }
+ if (other instanceof WeakKeyReference) {
+ return key.equals(((WeakKeyReference<?>) other).get());
+ }
+ return key.equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ private static final class WeakKeyReference<K> extends WeakReference<K> {
+ private final int hashCode;
+
+ private WeakKeyReference(K key, ReferenceQueue<K> queue) {
+ super(key, queue);
+ hashCode = key.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ K key = get();
+ if (key == null) {
+ return false;
+ }
+ if (other instanceof LookupKey) {
+ return key.equals(((LookupKey<?>) other).key);
+ }
+ if (other instanceof WeakKeyReference) {
+ return key.equals(((WeakKeyReference<?>) other).get());
+ }
+ return key.equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+ }
+
+ private static final class SoftValueReference<V> extends SoftReference<V> {
+ private final Object mapKey;
+
+ private SoftValueReference(V value, Object mapKey, ReferenceQueue<V>
queue) {
+ super(value, queue);
+ this.mapKey = mapKey;
+ }
+ }
+}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
index 044a9ac25..a6805c3ac 100644
--- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
+++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java
@@ -335,7 +335,9 @@ public final class ForyBuilder {
/**
* Whether pre-register guava types such as
`RegularImmutableMap`/`RegularImmutableList`. Those
- * types are not public API, but seems pretty stable.
+ * types are not public API, but seems pretty stable. When Guava is absent
at runtime, enabling
+ * this option still reserves the Guava internal id block so later internal
registrations keep the
+ * same ids.
*
* @see GuavaCollectionSerializers
*/
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index f82f6fd07..af4ec7f73 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -332,12 +332,7 @@ public class ClassResolver extends TypeResolver {
if (SqlTimeSerializers.isSqlModuleAvailable()) {
SqlTimeSerializers.registerDefaultSerializers(this);
} else {
- Preconditions.checkArgument(
- extRegistry.classIdGenerator +
SqlTimeSerializers.getNumReservedTypeIds()
- <= INTERNAL_NATIVE_ID_LIMIT,
- "Internal type id overflow: %s",
- extRegistry.classIdGenerator +
SqlTimeSerializers.getNumReservedTypeIds());
- extRegistry.classIdGenerator +=
SqlTimeSerializers.getNumReservedTypeIds();
+ reserveInternalTypeIds(SqlTimeSerializers.getNumReservedTypeIds());
}
OptionalSerializers.registerDefaultSerializers(this);
CollectionSerializers.registerDefaultSerializers(this);
@@ -362,7 +357,11 @@ public class ClassResolver extends TypeResolver {
ImmutableCollectionSerializers.registerSerializers(this);
SubListSerializers.registerSerializers(this, true);
if (config.registerGuavaTypes()) {
- GuavaCollectionSerializers.registerDefaultSerializers(this);
+ if (GuavaCollectionSerializers.isGuavaAvailable()) {
+ GuavaCollectionSerializers.registerDefaultSerializers(this);
+ } else {
+
reserveInternalTypeIds(GuavaCollectionSerializers.getNumReservedTypeIds());
+ }
}
if (config.deserializeUnknownClass()) {
if (metaContextShareEnabled) {
@@ -380,6 +379,25 @@ public class ClassResolver extends TypeResolver {
registerInternal(type);
}
+ private void reserveInternalTypeIds(int numTypeIds) {
+ Preconditions.checkArgument(numTypeIds >= 0, "numTypeIds must be
non-negative");
+ for (int i = 0; i < numTypeIds; i++) {
+ Preconditions.checkArgument(
+ extRegistry.classIdGenerator < INTERNAL_NATIVE_ID_LIMIT,
+ "Internal type id overflow: %s",
+ extRegistry.classIdGenerator);
+ while (extRegistry.classIdGenerator < typeIdToTypeInfo.length
+ && typeIdToTypeInfo[extRegistry.classIdGenerator] != null) {
+ extRegistry.classIdGenerator++;
+ }
+ Preconditions.checkArgument(
+ extRegistry.classIdGenerator < INTERNAL_NATIVE_ID_LIMIT,
+ "Internal type id overflow: %s",
+ extRegistry.classIdGenerator);
+ extRegistry.classIdGenerator++;
+ }
+ }
+
/** Register common class ahead to get smaller class id for serialization. */
private void registerCommonUsedClasses() {
registerInternal(LinkedList.class, TreeSet.class);
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
index b7bc39378..8e6b50484 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
@@ -19,7 +19,6 @@
package org.apache.fory.resolver;
-import com.google.common.collect.BiMap;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.Collections;
@@ -30,6 +29,7 @@ import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.fory.annotation.Internal;
import org.apache.fory.codegen.CodeGenerator;
+import org.apache.fory.collection.BiMap;
import org.apache.fory.collection.ConcurrentIdentityMap;
import org.apache.fory.collection.Tuple2;
import org.apache.fory.meta.EncodedMetaString;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
index f092cf52e..ef42e563a 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
@@ -22,8 +22,6 @@ package org.apache.fory.resolver;
import static org.apache.fory.type.TypeUtils.getSizeOfPrimitiveType;
import static org.apache.fory.type.Types.INVALID_USER_TYPE_ID;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
@@ -53,6 +51,7 @@ import org.apache.fory.builder.JITContext;
import org.apache.fory.codegen.CodeGenerator;
import org.apache.fory.codegen.Expression;
import org.apache.fory.codegen.Expression.Invoke;
+import org.apache.fory.collection.BiMap;
import org.apache.fory.collection.ConcurrentIdentityMap;
import org.apache.fory.collection.IdentityMap;
import org.apache.fory.collection.IdentityObjectIntMap;
@@ -1755,7 +1754,8 @@ public abstract class TypeResolver {
// shared across multiple fory instances.
IdentityHashMap<Class<?>, Integer> registeredClassIdMap =
new IdentityHashMap<>(isCrossLanguage() ? 4 : 200);
- BiMap<String, Class<?>> registeredClasses =
HashBiMap.create(isCrossLanguage() ? 4 : 200);
+ BiMap<String, Class<?>> registeredClasses =
+ BiMap.newHashIdentityBiMap(isCrossLanguage() ? 4 : 200);
final ConcurrentIdentityMap<Class<?>, TypeDef> currentLayerTypeDef;
// TODO(chaokunyang) Better to use soft reference, see ObjectStreamClass.
final ConcurrentHashMap<Tuple2<Class<?>, Boolean>, SortedMap<Member,
Descriptor>>
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
index 627075bc2..3e1437a9c 100644
--- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
+++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
@@ -21,8 +21,6 @@ package org.apache.fory.serializer;
import static org.apache.fory.util.function.Functions.makeGetterFunction;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -42,6 +40,8 @@ import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.regex.Pattern;
import org.apache.fory.Fory;
+import org.apache.fory.collection.Cache;
+import org.apache.fory.collection.CacheBuilder;
import org.apache.fory.collection.Tuple2;
import org.apache.fory.config.Config;
import org.apache.fory.context.CopyContext;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/GuavaCollectionSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/GuavaCollectionSerializers.java
index d7b03d88b..00e7cb2bb 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/GuavaCollectionSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/GuavaCollectionSerializers.java
@@ -34,11 +34,32 @@ import org.apache.fory.context.CopyContext;
import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
+import org.apache.fory.resolver.TypeInfo;
import org.apache.fory.resolver.TypeResolver;
/** Serializers for common guava types. */
@SuppressWarnings({"unchecked", "rawtypes"})
public class GuavaCollectionSerializers {
+ private static final String PKG = "com.google.common.collect";
+ private static final String IMMUTABLE_BI_MAP_CLASS_NAME = PKG +
".ImmutableBiMap";
+ private static final String IMMUTABLE_LIST_CLASS_NAME = PKG +
".ImmutableList";
+ private static final String IMMUTABLE_MAP_CLASS_NAME = PKG + ".ImmutableMap";
+ private static final String IMMUTABLE_SET_CLASS_NAME = PKG + ".ImmutableSet";
+ private static final String IMMUTABLE_SORTED_MAP_CLASS_NAME = PKG +
".ImmutableSortedMap";
+ private static final String IMMUTABLE_SORTED_SET_CLASS_NAME = PKG +
".ImmutableSortedSet";
+ private static final int NUM_RESERVED_TYPE_IDS = 13;
+ private static final boolean GUAVA_AVAILABLE =
+ isClassAvailable(IMMUTABLE_BI_MAP_CLASS_NAME)
+ && isClassAvailable(IMMUTABLE_LIST_CLASS_NAME)
+ && isClassAvailable(IMMUTABLE_MAP_CLASS_NAME)
+ && isClassAvailable(IMMUTABLE_SET_CLASS_NAME)
+ && isClassAvailable(IMMUTABLE_SORTED_MAP_CLASS_NAME)
+ && isClassAvailable(IMMUTABLE_SORTED_SET_CLASS_NAME);
+
+ private interface MapEntryBuilder {
+ void put(Object key, Object value);
+ }
+
abstract static class GuavaCollectionSerializer<T extends Collection>
extends CollectionSerializer<T> {
public GuavaCollectionSerializer(TypeResolver typeResolver, Class<T> cls) {
@@ -82,8 +103,6 @@ public class GuavaCollectionSerializers {
}
}
- private static final String pkg = "com.google.common.collect";
-
public static final class RegularImmutableListSerializer<T extends
ImmutableList>
extends GuavaCollectionSerializer<T> {
public RegularImmutableListSerializer(TypeResolver typeResolver, Class<T>
cls) {
@@ -192,7 +211,6 @@ public class GuavaCollectionSerializers {
}
abstract static class GuavaMapSerializer<T extends Map> extends
MapSerializer<T> {
-
public GuavaMapSerializer(TypeResolver typeResolver, Class<T> cls) {
super(typeResolver, cls, true);
typeResolver.setSerializer(cls, this);
@@ -211,7 +229,8 @@ public class GuavaCollectionSerializers {
@Override
public T copy(CopyContext copyContext, T originMap) {
Builder builder = makeBuilder(originMap.size());
- copyEntry(copyContext, originMap, builder);
+ GuavaCollectionSerializers.copyEntries(
+ typeResolver, mapTypeCache(), copyContext, originMap, builder::put);
return (T) builder.build();
}
@@ -264,6 +283,40 @@ public class GuavaCollectionSerializers {
return ImmutableBiMap.builder();
}
+ public static boolean isGuavaAvailable() {
+ return GUAVA_AVAILABLE;
+ }
+
+ public static int getNumReservedTypeIds() {
+ return NUM_RESERVED_TYPE_IDS;
+ }
+
+ private static void copyEntries(
+ TypeResolver typeResolver,
+ MapLikeSerializer.MapTypeCache state,
+ CopyContext copyContext,
+ Map<?, ?> originMap,
+ MapEntryBuilder builder) {
+ for (Map.Entry<?, ?> entry : originMap.entrySet()) {
+ Object key = entry.getKey();
+ if (key != null) {
+ TypeInfo typeInfo = typeResolver.getTypeInfo(key.getClass(),
state.keyTypeInfoWriteCache);
+ if (!typeInfo.getSerializer().isImmutable()) {
+ key = copyContext.copyObject(key, typeInfo.getTypeId());
+ }
+ }
+ Object value = entry.getValue();
+ if (value != null) {
+ TypeInfo typeInfo =
+ typeResolver.getTypeInfo(value.getClass(),
state.valueTypeInfoWriteCache);
+ if (!typeInfo.getSerializer().isImmutable()) {
+ value = copyContext.copyObject(value, typeInfo.getTypeId());
+ }
+ }
+ builder.put(key, value);
+ }
+ }
+
public static final class ImmutableMapSerializer<T extends ImmutableMap>
extends GuavaMapSerializer<T> {
@@ -329,7 +382,7 @@ public class GuavaCollectionSerializers {
public T copy(CopyContext copyContext, T originMap) {
Comparator comparator = copyContext.copyObject(originMap.comparator());
ImmutableSortedMap.Builder builder = new
ImmutableSortedMap.Builder(comparator);
- copyEntry(copyContext, originMap, builder);
+ copyEntries(typeResolver, mapTypeCache(), copyContext, originMap,
builder::put);
return (T) builder.build();
}
@@ -367,6 +420,9 @@ public class GuavaCollectionSerializers {
// UnmodifiableNavigableSet
public static void registerDefaultSerializers(TypeResolver resolver) {
+ if (!GUAVA_AVAILABLE) {
+ throw new IllegalStateException("Guava classes are not available");
+ }
// Note: Guava common types are not public API, don't register by
`ImmutableXXX.of()`,
// since different guava version may return different type objects, which
make class
// registration
@@ -374,31 +430,31 @@ public class GuavaCollectionSerializers {
// For example: guava 20 return ImmutableBiMap for ImmutableMap.of(), but
guava 27 return
// ImmutableMap.
Class cls =
- loadClass(pkg + ".RegularImmutableBiMap", ImmutableBiMap.of("k1", 1,
"k2", 4).getClass());
+ loadClass(PKG + ".RegularImmutableBiMap", ImmutableBiMap.of("k1", 1,
"k2", 4).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableBiMapSerializer(resolver, cls));
- cls = loadClass(pkg + ".SingletonImmutableBiMap", ImmutableBiMap.of(1,
2).getClass());
+ cls = loadClass(PKG + ".SingletonImmutableBiMap", ImmutableBiMap.of(1,
2).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableBiMapSerializer(resolver, cls));
- cls = loadClass(pkg + ".RegularImmutableMap", ImmutableMap.of("k1", 1,
"k2", 2).getClass());
+ cls = loadClass(PKG + ".RegularImmutableMap", ImmutableMap.of("k1", 1,
"k2", 2).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableMapSerializer(resolver, cls));
- cls = loadClass(pkg + ".RegularImmutableList",
ImmutableList.of().getClass());
+ cls = loadClass(PKG + ".RegularImmutableList",
ImmutableList.of().getClass());
resolver.registerInternalSerializer(cls, new
RegularImmutableListSerializer(resolver, cls));
- cls = loadClass(pkg + ".SingletonImmutableList",
ImmutableList.of(1).getClass());
+ cls = loadClass(PKG + ".SingletonImmutableList",
ImmutableList.of(1).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableListSerializer(resolver, cls));
- cls = loadClass(pkg + ".RegularImmutableSet", ImmutableSet.of(1,
2).getClass());
+ cls = loadClass(PKG + ".RegularImmutableSet", ImmutableSet.of(1,
2).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSetSerializer(resolver, cls));
- cls = loadClass(pkg + ".SingletonImmutableSet",
ImmutableSet.of(1).getClass());
+ cls = loadClass(PKG + ".SingletonImmutableSet",
ImmutableSet.of(1).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSetSerializer(resolver, cls));
// sorted set/map doesn't support xlang.
- cls = loadClass(pkg + ".RegularImmutableSortedSet",
ImmutableSortedSet.of(1, 2).getClass());
+ cls = loadClass(PKG + ".RegularImmutableSortedSet",
ImmutableSortedSet.of(1, 2).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSortedSetSerializer<>(resolver, cls));
- cls = loadClass(pkg + ".ImmutableSortedMap", ImmutableSortedMap.of(1,
2).getClass());
+ cls = loadClass(PKG + ".ImmutableSortedMap", ImmutableSortedMap.of(1,
2).getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSortedMapSerializer<>(resolver, cls));
// Guava version before 19.0, of() return
//
EmptyImmutableSet/EmptyImmutableBiMap/EmptyImmutableSortedMap/EmptyImmutableSortedSet
// we register if class exist or register empty to deserialize.
- if (checkClassExist(pkg + ".EmptyImmutableSet")) {
- cls = loadClass(pkg + ".EmptyImmutableSet",
ImmutableSet.of().getClass());
+ if (checkClassExist(PKG + ".EmptyImmutableSet")) {
+ cls = loadClass(PKG + ".EmptyImmutableSet",
ImmutableSet.of().getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSetSerializer(resolver, cls));
} else {
class GuavaEmptySet {}
@@ -406,8 +462,8 @@ public class GuavaCollectionSerializers {
cls = GuavaEmptySet.class;
resolver.registerInternalSerializer(cls, new
ImmutableSetSerializer(resolver, cls));
}
- if (checkClassExist(pkg + ".EmptyImmutableBiMap")) {
- cls = loadClass(pkg + ".EmptyImmutableBiMap",
ImmutableBiMap.of().getClass());
+ if (checkClassExist(PKG + ".EmptyImmutableBiMap")) {
+ cls = loadClass(PKG + ".EmptyImmutableBiMap",
ImmutableBiMap.of().getClass());
resolver.registerInternalSerializer(cls, new
ImmutableMapSerializer(resolver, cls));
} else {
class GuavaEmptyBiMap {}
@@ -415,8 +471,8 @@ public class GuavaCollectionSerializers {
cls = GuavaEmptyBiMap.class;
resolver.registerInternalSerializer(cls, new
ImmutableMapSerializer(resolver, cls));
}
- if (checkClassExist(pkg + ".EmptyImmutableSortedSet")) {
- cls = loadClass(pkg + ".EmptyImmutableSortedSet",
ImmutableSortedSet.of().getClass());
+ if (checkClassExist(PKG + ".EmptyImmutableSortedSet")) {
+ cls = loadClass(PKG + ".EmptyImmutableSortedSet",
ImmutableSortedSet.of().getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSortedSetSerializer(resolver, cls));
} else {
class GuavaEmptySortedSet {}
@@ -424,8 +480,8 @@ public class GuavaCollectionSerializers {
cls = GuavaEmptySortedSet.class;
resolver.registerInternalSerializer(cls, new
ImmutableSortedSetSerializer(resolver, cls));
}
- if (checkClassExist(pkg + ".EmptyImmutableSortedMap")) {
- cls = loadClass(pkg + ".EmptyImmutableSortedMap",
ImmutableSortedMap.of().getClass());
+ if (checkClassExist(PKG + ".EmptyImmutableSortedMap")) {
+ cls = loadClass(PKG + ".EmptyImmutableSortedMap",
ImmutableSortedMap.of().getClass());
resolver.registerInternalSerializer(cls, new
ImmutableSortedMapSerializer(resolver, cls));
} else {
class GuavaEmptySortedMap {}
@@ -440,7 +496,7 @@ public class GuavaCollectionSerializers {
return cache;
} else {
try {
- return Class.forName(className);
+ return Class.forName(className, false,
GuavaCollectionSerializers.class.getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
@@ -448,8 +504,12 @@ public class GuavaCollectionSerializers {
}
static boolean checkClassExist(String className) {
+ return isClassAvailable(className);
+ }
+
+ private static boolean isClassAvailable(String className) {
try {
- Class.forName(className);
+ Class.forName(className, false,
GuavaCollectionSerializers.class.getClassLoader());
} catch (ClassNotFoundException e) {
return false;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapLikeSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapLikeSerializer.java
index 59c89cc12..81c0dc5aa 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapLikeSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/MapLikeSerializer.java
@@ -32,7 +32,6 @@ import static
org.apache.fory.serializer.collection.MapFlags.VALUE_DECL_TYPE;
import static org.apache.fory.serializer.collection.MapFlags.VALUE_HAS_NULL;
import static org.apache.fory.type.TypeUtils.MAP_TYPE;
-import com.google.common.collect.ImmutableMap.Builder;
import java.lang.invoke.MethodHandle;
import java.util.Iterator;
import java.util.Map;
@@ -596,30 +595,6 @@ public abstract class MapLikeSerializer<T> extends
Serializer<T> {
}
}
- protected <K, V> void copyEntry(
- CopyContext copyContext, Map<K, V> originMap, Builder<K, V> builder) {
- TypeResolver classResolver = typeResolver;
- MapTypeCache state = mapTypeCache();
- for (Entry<K, V> entry : originMap.entrySet()) {
- K key = entry.getKey();
- if (key != null) {
- TypeInfo typeInfo = classResolver.getTypeInfo(key.getClass(),
state.keyTypeInfoWriteCache);
- if (!typeInfo.getSerializer().isImmutable()) {
- key = copyContext.copyObject(key, typeInfo.getTypeId());
- }
- }
- V value = entry.getValue();
- if (value != null) {
- TypeInfo typeInfo =
- classResolver.getTypeInfo(value.getClass(),
state.valueTypeInfoWriteCache);
- if (!typeInfo.getSerializer().isImmutable()) {
- value = copyContext.copyObject(value, typeInfo.getTypeId());
- }
- }
- builder.put(key, value);
- }
- }
-
protected <K, V> void copyEntry(CopyContext copyContext, Map<K, V>
originMap, Object[] elements) {
TypeResolver classResolver = typeResolver;
MapTypeCache state = mapTypeCache();
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/converter/FieldConverters.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/converter/FieldConverters.java
index 87f7cdd5a..fc602f512 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/converter/FieldConverters.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/converter/FieldConverters.java
@@ -19,9 +19,9 @@
package org.apache.fory.serializer.converter;
-import com.google.common.collect.ImmutableSet;
import java.lang.reflect.Field;
import java.util.Set;
+import org.apache.fory.collection.Collections;
import org.apache.fory.reflect.FieldAccessor;
import org.apache.fory.type.TypeUtils;
@@ -31,6 +31,9 @@ import org.apache.fory.type.TypeUtils;
* automatic type conversion during serialization/deserialization processes.
*/
public class FieldConverters {
+ private static Set<Class<?>> compatibleTypes(Class<?>... types) {
+ return java.util.Collections.unmodifiableSet(Collections.ofHashSet(types));
+ }
/**
* Creates an appropriate field converter based on the target field type and
source object type.
@@ -128,7 +131,7 @@ public class FieldConverters {
* false for null values and incompatible types.
*/
public static class BooleanConverter extends FieldConverter<Boolean> {
- static Set<Class<?>> compatibleTypes = ImmutableSet.of(String.class,
Boolean.class);
+ static Set<Class<?>> compatibleTypes = compatibleTypes(String.class,
Boolean.class);
protected BooleanConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -193,7 +196,7 @@ public class FieldConverters {
*/
public static class ByteConverter extends FieldConverter<Byte> {
static Set<Class<?>> compatibleTypes =
- ImmutableSet.of(String.class, Integer.class, Long.class, Short.class,
Byte.class);
+ compatibleTypes(String.class, Integer.class, Long.class, Short.class,
Byte.class);
protected ByteConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -266,7 +269,7 @@ public class FieldConverters {
*/
public static class ShortConverter extends FieldConverter<Short> {
static Set<Class<?>> compatibleTypes =
- ImmutableSet.of(String.class, Integer.class, Long.class, Short.class);
+ compatibleTypes(String.class, Integer.class, Long.class, Short.class);
protected ShortConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -330,7 +333,7 @@ public class FieldConverters {
* values.
*/
public static class IntConverter extends FieldConverter<Integer> {
- static Set<Class<?>> compatibleTypes = ImmutableSet.of(String.class,
Long.class, Integer.class);
+ static Set<Class<?>> compatibleTypes = compatibleTypes(String.class,
Long.class, Integer.class);
protected IntConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -398,7 +401,7 @@ public class FieldConverters {
* null values.
*/
public static class LongConverter extends FieldConverter<Long> {
- static Set<Class<?>> compatibleTypes = ImmutableSet.of(String.class,
Long.class);
+ static Set<Class<?>> compatibleTypes = compatibleTypes(String.class,
Long.class);
protected LongConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -463,7 +466,7 @@ public class FieldConverters {
* for null values. Only allows conversion from String.
*/
public static class FloatConverter extends FieldConverter<Float> {
- static Set<Class<?>> compatibleTypes = ImmutableSet.of(String.class,
Float.class);
+ static Set<Class<?>> compatibleTypes = compatibleTypes(String.class,
Float.class);
protected FloatConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -528,7 +531,7 @@ public class FieldConverters {
* for null values. Allows conversion from String and Float.
*/
public static class DoubleConverter extends FieldConverter<Double> {
- static Set<Class<?>> compatibleTypes = ImmutableSet.of(String.class,
Float.class, Double.class);
+ static Set<Class<?>> compatibleTypes = compatibleTypes(String.class,
Float.class, Double.class);
protected DoubleConverter(FieldAccessor fieldAccessor) {
super(fieldAccessor);
@@ -596,7 +599,7 @@ public class FieldConverters {
*/
public static class StringConverter extends FieldConverter<String> {
static Set<Class<?>> compatibleTypes =
- ImmutableSet.of(
+ compatibleTypes(
Integer.class,
Long.class,
Short.class,
diff --git a/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
b/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
index 8a8a4f1b5..5ec94eaea 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/Descriptor.java
@@ -21,8 +21,6 @@ package org.apache.fory.type;
import static org.apache.fory.util.Preconditions.checkArgument;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
@@ -59,6 +57,8 @@ import org.apache.fory.annotation.Uint64ArrayType;
import org.apache.fory.annotation.Uint64Type;
import org.apache.fory.annotation.Uint8ArrayType;
import org.apache.fory.annotation.Uint8Type;
+import org.apache.fory.collection.Cache;
+import org.apache.fory.collection.CacheBuilder;
import org.apache.fory.collection.Collections;
import org.apache.fory.collection.Tuple2;
import org.apache.fory.memory.Platform;
diff --git
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
index 04906f123..2f67bfc3b 100644
---
a/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
+++
b/java/fory-core/src/main/resources/META-INF/native-image/org.apache.fory/fory-core/native-image.properties
@@ -17,62 +17,18 @@
#
https://www.graalvm.org/latest/reference-manual/native-image/dynamic-features/Reflection/#unsafe-accesses
:
# The unsafe offset get on build time may be different from runtime
-Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
+Args=--initialize-at-build-time=org.apache.fory.collection.BiMap,\
+ org.apache.fory.collection.BiMap$Inverse,\
+ org.apache.fory.collection.CacheBuilder$LocalCache,\
+ org.apache.fory.collection.ReferenceConcurrentMap,\
+ org.apache.fory.collection.ReferenceConcurrentMap$LookupKey,\
+ org.apache.fory.collection.ReferenceConcurrentMap$SoftValueReference,\
+ org.apache.fory.collection.ReferenceConcurrentMap$WeakKeyReference,\
+ org.apache.fory.memory.MemoryBuffer,\
+ org.apache.fory.serializer.collection.GuavaCollectionSerializers,\
org.apache.fory.serializer.struct.Fingerprint,\
org.apache.fory.serializer.Float16Serializer,\
org.apache.fory.serializer.PrimitiveSerializers$Float16Serializer,\
- com.google.common.base.Equivalence$Equals,\
- com.google.common.base.Equivalence$Identity,\
- com.google.common.base.Equivalence,\
- com.google.common.base.FinalizableReferenceQueue,\
- com.google.common.base.Ticker$1,\
- com.google.common.base.internal.Finalizer,\
- com.google.common.cache.CacheBuilder$1,\
- com.google.common.cache.CacheBuilder$2,\
- com.google.common.cache.CacheBuilder$3,\
- com.google.common.cache.CacheBuilder$NullListener,\
- com.google.common.cache.CacheBuilder$OneWeigher,\
- com.google.common.cache.LocalCache$1,\
- com.google.common.cache.LocalCache$2,\
- com.google.common.cache.LocalCache$EntryFactory$1,\
- com.google.common.cache.LocalCache$EntryFactory$2,\
- com.google.common.cache.LocalCache$EntryFactory$3,\
- com.google.common.cache.LocalCache$EntryFactory$4,\
- com.google.common.cache.LocalCache$EntryFactory$5,\
- com.google.common.cache.LocalCache$EntryFactory$6,\
- com.google.common.cache.LocalCache$EntryFactory$7,\
- com.google.common.cache.LocalCache$EntryFactory$8,\
- com.google.common.cache.LocalCache$EntrySet,\
- com.google.common.cache.LocalCache$LocalManualCache,\
- com.google.common.cache.LocalCache$Segment,\
- com.google.common.cache.LocalCache$SoftValueReference,\
- com.google.common.cache.LocalCache$Strength$1,\
- com.google.common.cache.LocalCache$Strength$2,\
- com.google.common.cache.LocalCache$Strength$3,\
- com.google.common.cache.LocalCache$StrongEntry,\
- com.google.common.cache.LocalCache$StrongValueReference,\
- com.google.common.cache.LocalCache$WeakEntry,\
- com.google.common.cache.LocalCache$WeakValueReference,\
- com.google.common.cache.LocalCache,\
- com.google.common.collect.AbstractIterator$1,\
- com.google.common.collect.ImmutableSortedMap,\
- com.google.common.collect.NaturalOrdering,\
- com.google.common.collect.Platform,\
- com.google.common.collect.RegularImmutableBiMap,\
- com.google.common.collect.RegularImmutableList,\
- com.google.common.collect.RegularImmutableMap,\
- com.google.common.collect.RegularImmutableSet,\
- com.google.common.collect.RegularImmutableSortedSet,\
- com.google.common.collect.SingletonImmutableBiMap,\
- com.google.common.collect.SingletonImmutableList,\
- com.google.common.collect.SingletonImmutableSet,\
- com.google.common.collect.HashBiMap,\
- com.google.common.collect.HashBiMap$BiEntry,\
- com.google.common.collect.HashBiMap$Inverse,\
- com.google.common.math.IntMath$1,\
- com.google.common.primitives.Primitives,\
- com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper,\
- com.google.common.util.concurrent.SettableFuture,\
java.io.IOException,\
java.lang.ArrayIndexOutOfBoundsException,\
java.lang.Boolean,\
@@ -462,21 +418,6 @@
Args=--initialize-at-build-time=org.apache.fory.memory.MemoryBuffer,\
org.apache.fory.serializer.collection.CollectionSerializers$XlangSetDefaultSerializer,\
org.apache.fory.serializer.collection.ForyArrayAsListSerializer$ArrayAsList,\
org.apache.fory.serializer.collection.ForyArrayAsListSerializer,\
- org.apache.fory.serializer.collection.GuavaCollectionSerializers$1,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$1GuavaEmptyBiMap,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$1GuavaEmptySet,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$1GuavaEmptySortedMap,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$1GuavaEmptySortedSet,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$GuavaCollectionSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$GuavaMapSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableBiMapSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableListSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableMapSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableSetSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableSortedMapSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$ImmutableSortedSetSerializer,\
-
org.apache.fory.serializer.collection.GuavaCollectionSerializers$RegularImmutableListSerializer,\
- org.apache.fory.serializer.collection.GuavaCollectionSerializers,\
org.apache.fory.serializer.collection.ImmutableCollectionSerializers$ImmutableListSerializer,\
org.apache.fory.serializer.collection.ImmutableCollectionSerializers$ImmutableMapSerializer,\
org.apache.fory.serializer.collection.ImmutableCollectionSerializers$ImmutableSetSerializer,\
diff --git
a/java/fory-core/src/test/java/org/apache/fory/GuavaOptionalDependencyTest.java
b/java/fory-core/src/test/java/org/apache/fory/GuavaOptionalDependencyTest.java
new file mode 100644
index 000000000..0cda0ea87
--- /dev/null
+++
b/java/fory-core/src/test/java/org/apache/fory/GuavaOptionalDependencyTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package org.apache.fory;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.apache.fory.config.Language;
+import org.apache.fory.resolver.ClassResolver;
+import org.apache.fory.serializer.collection.GuavaCollectionSerializers;
+import org.testng.annotations.Test;
+
+public class GuavaOptionalDependencyTest {
+ private static final String RESULT_PREFIX = "RESULT:";
+
+ @Test
+ public void testBuildWithoutGuavaAndReserveIds() throws Exception {
+ assertTrue(GuavaCollectionSerializers.isGuavaAvailable());
+ RegistrationIds inProcessIds = currentProcessIds();
+ assertEquals(
+ inProcessIds.enabledId - inProcessIds.disabledId,
+ GuavaCollectionSerializers.getNumReservedTypeIds());
+ RegistrationIds childIds = runWithoutGuava();
+ assertEquals(childIds.enabledId, inProcessIds.enabledId);
+ assertEquals(childIds.disabledId, inProcessIds.disabledId);
+ }
+
+ private static RegistrationIds currentProcessIds() {
+ return new RegistrationIds(registeredInternalId(true),
registeredInternalId(false));
+ }
+
+ private static int registeredInternalId(boolean registerGuavaTypes) {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.JAVA)
+ .registerGuavaTypes(registerGuavaTypes)
+ .requireClassRegistration(false)
+ .suppressClassRegistrationWarnings(true)
+ .build();
+ ClassResolver resolver = (ClassResolver) fory.getTypeResolver();
+ resolver.registerInternal(InternalSample.class);
+ return resolver.getRegisteredClassId(InternalSample.class);
+ }
+
+ private static RegistrationIds runWithoutGuava() throws Exception {
+ String javaBin =
+ System.getProperty("java.home") + File.separator + "bin" +
File.separator + "java";
+ String filteredClassPath =
removeGuavaFromClasspath(System.getProperty("java.class.path"));
+ Process process =
+ new ProcessBuilder(javaBin, "-cp", filteredClassPath,
NoGuavaMain.class.getName())
+ .redirectErrorStream(true)
+ .start();
+ String output = readFully(process.getInputStream());
+ assertEquals(process.waitFor(), 0, output);
+ return parseResult(output);
+ }
+
+ private static RegistrationIds parseResult(String output) {
+ for (String line : output.split("\\R")) {
+ if (line.startsWith(RESULT_PREFIX)) {
+ String[] parts = line.substring(RESULT_PREFIX.length()).split(",");
+ return new RegistrationIds(Integer.parseInt(parts[0]),
Integer.parseInt(parts[1]));
+ }
+ }
+ throw new AssertionError("Missing result line in output:\n" + output);
+ }
+
+ private static String removeGuavaFromClasspath(String classPath) {
+ return
Arrays.stream(classPath.split(java.util.regex.Pattern.quote(File.pathSeparator)))
+ .filter(path -> !new File(path).getName().startsWith("guava-"))
+ .collect(Collectors.joining(File.pathSeparator));
+ }
+
+ private static String readFully(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, read);
+ }
+ return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
+ }
+
+ private static final class RegistrationIds {
+ private final int enabledId;
+ private final int disabledId;
+
+ private RegistrationIds(int enabledId, int disabledId) {
+ this.enabledId = enabledId;
+ this.disabledId = disabledId;
+ }
+ }
+
+ public static final class NoGuavaMain {
+ public static void main(String[] args) {
+ RegistrationIds ids = currentProcessIds();
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.JAVA)
+ .registerGuavaTypes(true)
+ .requireClassRegistration(false)
+ .suppressClassRegistrationWarnings(true)
+ .build();
+ byte[] bytes = fory.serialize(new SampleValue("fory"));
+ SampleValue value = (SampleValue) fory.deserialize(bytes);
+ if (!"fory".equals(value.value)) {
+ throw new AssertionError("Unexpected round-trip value " + value.value);
+ }
+ System.out.println(RESULT_PREFIX + ids.enabledId + "," + ids.disabledId);
+ }
+ }
+
+ public static final class InternalSample {}
+
+ public static final class SampleValue {
+ private final String value;
+
+ public SampleValue(String value) {
+ this.value = value;
+ }
+ }
+}
diff --git a/java/fory-format/pom.xml b/java/fory-format/pom.xml
index 679ecdad0..9e89f5e26 100644
--- a/java/fory-format/pom.xml
+++ b/java/fory-format/pom.xml
@@ -90,6 +90,11 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/java/fory-testsuite/pom.xml b/java/fory-testsuite/pom.xml
index 0863dfe01..a743ecead 100644
--- a/java/fory-testsuite/pom.xml
+++ b/java/fory-testsuite/pom.xml
@@ -111,6 +111,11 @@
<version>1.2.83</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
similarity index 74%
rename from
java/fory-core/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
rename to
java/fory-testsuite/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
index a1fb62fe9..ff9458c07 100644
---
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
+++
b/java/fory-testsuite/src/test/java/org/apache/fory/serializer/collection/GuavaCollectionSerializersTest.java
@@ -26,15 +26,57 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.Data;
+import java.util.Objects;
import org.apache.fory.Fory;
-import org.apache.fory.ForyTestBase;
+import org.apache.fory.TestBase;
import org.apache.fory.config.Language;
import org.testng.Assert;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
-public class GuavaCollectionSerializersTest extends ForyTestBase {
+public class GuavaCollectionSerializersTest extends TestBase {
+ @DataProvider(name = "trackingRefFory")
+ public static Object[][] trackingRefFory() {
+ return new Object[][] {{newJavaFory(true, false)}, {newJavaFory(false,
false)}};
+ }
+
+ @DataProvider(name = "foryCopyConfig")
+ public static Object[][] foryCopyConfig() {
+ return new Object[][] {{newCopyFory(false)}, {newCopyFory(true)}};
+ }
+
+ @DataProvider(name = "javaFory")
+ public static Object[][] javaFory() {
+ return new Object[][] {
+ {newJavaFory(true, false)},
+ {newJavaFory(false, false)},
+ {newJavaFory(true, true)},
+ {newJavaFory(false, true)}
+ };
+ }
+
+ private static Fory newJavaFory(boolean trackingRef, boolean codegen) {
+ return builder()
+ .withRefTracking(trackingRef)
+ .withCodegen(codegen)
+ .suppressClassRegistrationWarnings(true)
+ .build();
+ }
+
+ private static Fory newCopyFory(boolean codegen) {
+ return builder()
+ .withRefCopy(true)
+ .withJdkClassSerializableCheck(false)
+ .withCodegen(codegen)
+ .suppressClassRegistrationWarnings(true)
+ .build();
+ }
+
+ private static void copyCheck(Fory fory, Object obj) {
+ Object copy = fory.copy(obj);
+ Assert.assertEquals(copy, obj);
+ Assert.assertNotSame(copy, obj);
+ }
@Test(dataProvider = "trackingRefFory")
public void testImmutableListSerializer(Fory fory) {
@@ -153,8 +195,13 @@ public class GuavaCollectionSerializersTest extends
ForyTestBase {
}
@Test
- public void tesXlangSerialize() {
- Fory fory = Fory.builder().withLanguage(Language.XLANG).build();
+ public void testXlangSerialize() {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.XLANG)
+ .requireClassRegistration(false)
+ .suppressClassRegistrationWarnings(true)
+ .build();
serDe(fory, ImmutableBiMap.of());
serDe(fory, ImmutableBiMap.of(1, 2));
serDe(fory, ImmutableBiMap.of(1, 2, 3, 4));
@@ -168,23 +215,45 @@ public class GuavaCollectionSerializersTest extends
ForyTestBase {
serDe(fory, ImmutableSet.of(1, 2, 3, 4));
}
- @Data
- @AllArgsConstructor
- public static class Pojo {
- List<List<Object>> data;
- }
-
@Test(dataProvider = "javaFory")
- void testNestedRefTracking(Fory fory) {
+ public void testNestedRefTracking(Fory fory) {
Pojo pojo = new Pojo(ImmutableList.of(ImmutableList.of(1, 2),
ImmutableList.of(2, 2)));
- byte[] bytes = fory.serialize(pojo);
- Pojo deserializedPojo = (Pojo) fory.deserialize(bytes);
- System.out.println(deserializedPojo);
+ Assert.assertEquals(serDe(fory, pojo), pojo);
}
@Test(dataProvider = "foryCopyConfig")
- void testNestedRefTrackingCopy(Fory fory) {
+ public void testNestedRefTrackingCopy(Fory fory) {
Pojo pojo = new Pojo(ImmutableList.of(ImmutableList.of(1, 2),
ImmutableList.of(2, 2)));
copyCheck(fory, pojo);
}
+
+ public static final class Pojo {
+ private final List<List<Object>> data;
+
+ public Pojo(List<List<Object>> data) {
+ this.data = data;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Pojo)) {
+ return false;
+ }
+ Pojo pojo = (Pojo) other;
+ return Objects.equals(data, pojo.data);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(data);
+ }
+
+ @Override
+ public String toString() {
+ return "Pojo{" + "data=" + data + '}';
+ }
+ }
}
diff --git
a/scala/src/main/java/org/apache/fory/serializer/scala/ScalaDispatcher.java
b/scala/src/main/java/org/apache/fory/serializer/scala/ScalaDispatcher.java
index d4294d6dc..e2af2a2ef 100644
--- a/scala/src/main/java/org/apache/fory/serializer/scala/ScalaDispatcher.java
+++ b/scala/src/main/java/org/apache/fory/serializer/scala/ScalaDispatcher.java
@@ -19,11 +19,11 @@
package org.apache.fory.serializer.scala;
-import com.google.common.base.Preconditions;
import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.serializer.JavaSerializer;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.SerializerFactory;
+import org.apache.fory.util.Preconditions;
import scala.collection.generic.DefaultSerializable;
import java.lang.reflect.Method;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]