diff --git a/java/fory-core/src/main/java/org/apache/fory/platform/GraalvmSupport.java b/java/fory-core/src/main/java/org/apache/fory/platform/GraalvmSupport.java index 6a5a047134..a86f628892 100644 --- a/java/fory-core/src/main/java/org/apache/fory/platform/GraalvmSupport.java +++ b/java/fory-core/src/main/java/org/apache/fory/platform/GraalvmSupport.java @@ -39,6 +39,7 @@ import org.apache.fory.serializer.JavaSerializer; import org.apache.fory.serializer.JdkProxySerializer; import org.apache.fory.serializer.LambdaSerializer; +import org.apache.fory.serializer.NonSerializableSerializer; import org.apache.fory.serializer.ObjectSerializer; import org.apache.fory.serializer.ObjectStreamSerializer; import org.apache.fory.serializer.PrimitiveArraySerializers; @@ -137,6 +138,7 @@ public class GraalvmSupport { registerDefaultSerializerClass(ObjectStreamSerializer.class); registerDefaultSerializerClass(JavaSerializer.class); registerDefaultSerializerClass(ObjectSerializer.class); + registerDefaultSerializerClass(NonSerializableSerializer.class); } /** Returns true if current process is running in graalvm native image build stage. */ 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 bbe742a82a..93a9fbc97c 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 @@ -124,6 +124,7 @@ import org.apache.fory.serializer.JdkProxySerializer; import org.apache.fory.serializer.LambdaSerializer; import org.apache.fory.serializer.LocaleSerializer; +import org.apache.fory.serializer.NonSerializableSerializer; import org.apache.fory.serializer.NoneSerializer; import org.apache.fory.serializer.ObjectSerializer; import org.apache.fory.serializer.OptionalSerializers; @@ -1502,8 +1503,7 @@ public Class getSerializerClass(Class cls, boolean code } if (config.checkJdkClassSerializable()) { if (cls.getName().startsWith("java") && !(Serializable.class.isAssignableFrom(cls))) { - throw new UnsupportedOperationException( - String.format("Class %s doesn't support serialization.", cls)); + return NonSerializableSerializer.class; } } if (ScalaTypes.SCALA_AVAILABLE && ReflectionUtils.isScalaSingletonObject(cls)) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/NonSerializableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/NonSerializableSerializer.java new file mode 100644 index 0000000000..b1f10178e1 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/NonSerializableSerializer.java @@ -0,0 +1,50 @@ +/* + * 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.serializer; + +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; + +/** + * Serializer for non-serializable JDK types that are retained by reference during copy. + * + *

These objects can contain JVM-managed state, so copying their fields is unsupported. + */ +public final class NonSerializableSerializer extends Serializer { + public NonSerializableSerializer(Config config, Class type) { + super(config, type, true); + } + + @Override + public void write(WriteContext writeContext, T value) { + throw unsupportedOperation(); + } + + @Override + public T read(ReadContext readContext) { + throw unsupportedOperation(); + } + + private UnsupportedOperationException unsupportedOperation() { + return new UnsupportedOperationException( + String.format("Class %s doesn't support serialization.", type)); + } +} diff --git a/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java b/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java index bd4cd7d418..02f2a2162c 100644 --- a/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/ForyCopyTest.java @@ -79,10 +79,12 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; import org.apache.fory.collection.LazyMap; +import org.apache.fory.config.Language; import org.apache.fory.context.CopyContext; import org.apache.fory.context.ReadContext; import org.apache.fory.context.WriteContext; import org.apache.fory.exception.ForyException; +import org.apache.fory.exception.SerializationException; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.EnumSerializerTest; import org.apache.fory.serializer.EnumSerializerTest.EnumFoo; @@ -210,6 +212,35 @@ public void testCopyOnlySerializerStillRejectsSerialize() { Assert.assertThrows(ForyException.class, () -> fory.serialize(beanA)); } + @Test + public void testCopyNonSerializableJdkObject() { + Fory fory = + builder() + .withLanguage(Language.JAVA) + .withRefTracking(true) + .withRefCopy(true) + .requireClassRegistration(false) + .build(); + Package packageValue = String.class.getPackage(); + + Assert.assertSame(fory.copy(packageValue), packageValue); + + Object[] values = new Object[] {packageValue}; + Object[] copiedValues = fory.copy(values); + Assert.assertNotSame(copiedValues, values); + Assert.assertSame(copiedValues[0], packageValue); + } + + @Test + public void testSerializeNonSerializableJdkObjectStillFails() { + Fory fory = builder().withLanguage(Language.JAVA).requireClassRegistration(false).build(); + Package packageValue = String.class.getPackage(); + + SerializationException exception = + Assert.expectThrows(SerializationException.class, () -> fory.serialize(packageValue)); + Assert.assertTrue(exception.getCause() instanceof UnsupportedOperationException); + } + @Test public void testCopyFailureDoesNotLeakCopyDepth() { Fory fory =