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 de036f9759..c054f1cec3 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; @@ -140,6 +141,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 fe58520121..5a86cd7093 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 @@ -127,6 +127,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; @@ -1498,9 +1499,8 @@ 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)); + if (cls.getName().startsWith("java") && !Serializable.class.isAssignableFrom(cls)) { + return NonSerializableSerializer.class; } } if (ScalaTypes.SCALA_AVAILABLE && ReflectionUtils.isScalaSingletonObject(cls)) { @@ -1513,8 +1513,6 @@ public Class getSerializerClass(Class cls, boolean code } } if (isCollection(cls)) { - // Serializer of common collection such as ArrayList/LinkedList should be registered - // already. serializerClass = ChildContainerSerializers.getCollectionSerializerClass(cls); if (serializerClass != null) { return serializerClass; 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..497cc9cb18 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/NonSerializableSerializer.java @@ -0,0 +1,52 @@ +/* + * 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.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; + +/** + * Serializer for non-Serializable JDK classes. Binary serialization is unsupported — {@link + * #write}/{@link #read} throw {@link UnsupportedOperationException} — while copy reuses {@link + * AbstractObjectSerializer}'s field-copy implementation. + * + *

Distinct from {@link CopyOnlyObjectSerializer}, which blocks serialization for + * registration-security reasons (remediable by registering the class). Here serialization is + * intrinsically unsupported and not remediable by registration, so the failure semantics differ + * deliberately. + */ +public final class NonSerializableSerializer extends AbstractObjectSerializer { + public NonSerializableSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type); + } + + @Override + public void write(WriteContext writeContext, T value) { + throw new UnsupportedOperationException( + String.format("Class %s doesn't support serialization.", type.getName())); + } + + @Override + public T read(ReadContext readContext) { + throw new UnsupportedOperationException( + String.format("Class %s doesn't support serialization.", type.getName())); + } +} 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..8c7685ad12 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,13 +79,16 @@ 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; +import org.apache.fory.serializer.NonSerializableSerializer; import org.apache.fory.serializer.Serializer; import org.apache.fory.serializer.collection.ChildContainerSerializersTest.ChildArrayDeque; import org.apache.fory.serializer.collection.SynchronizedSerializersTest; @@ -454,4 +457,29 @@ public void testComplexCollectionCopy() { assertEquals(fory.copy(collectionFields).toCanEqual(), collectionFields.toCanEqual()); } } + + @Test + public void testCopyNonSerializableJdkClass() { + Fory fory = Fory.builder().withLanguage(Language.JAVA).requireClassRegistration(false).build(); + Class serializerClass = + fory.getTypeResolver().getSerializerClass(java.lang.Package.class); + Assert.assertSame(serializerClass, NonSerializableSerializer.class); + } + + @Test + public void testSerializeNonSerializableJdkClassStillThrows() { + Fory fory = + Fory.builder() + .withLanguage(Language.JAVA) + .withRefCopy(true) + .requireClassRegistration(false) + .build(); + try { + fory.serialize(String.class.getPackage()); + Assert.fail("Expected serialization of java.lang.Package to fail"); + } catch (SerializationException e) { + Assert.assertTrue(e.getCause() instanceof UnsupportedOperationException); + Assert.assertTrue(e.getMessage().contains("doesn't support serialization")); + } + } }