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 253c14b507..c156cee831 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 @@ -116,7 +116,6 @@ import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.serializer.ArraySerializers; import org.apache.fory.serializer.BufferSerializers; -import org.apache.fory.serializer.CodegenSerializer.LazyInitBeanSerializer; import org.apache.fory.serializer.CompatibleSerializer; import org.apache.fory.serializer.CopyOnlyObjectSerializer; import org.apache.fory.serializer.EnumSerializer; @@ -127,6 +126,8 @@ 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.CodegenSerializer.LazyInitBeanSerializer; import org.apache.fory.serializer.NoneSerializer; import org.apache.fory.serializer.ObjectSerializer; import org.apache.fory.serializer.OptionalSerializers; @@ -1498,9 +1499,10 @@ 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)){ + // Route to a serializer that still refuses binary serialization (write/read throw) but + // supports field copy via AbstractObjectSerializer + 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..8fa9d45917 --- /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..68442e9d4d 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; @@ -454,4 +456,36 @@ public void testComplexCollectionCopy() { assertEquals(fory.copy(collectionFields).toCanEqual(), collectionFields.toCanEqual()); } } + + @Test + public void testCopyNonSerializableJdkClass(){ + + Fory fory = Fory.builder() + .withLanguage(Language.JAVA) + .requireClassRegistration(false) + .build(); + Package pkg = String.class.getPackage(); + Package copy = fory.copy(pkg); + Assert.assertNotNull(copy); + Assert.assertEquals(copy.getName(), pkg.getName()); + } + + + @Test + public void testSerializeNonSerializableJdkClassStillThrows() { + // Serializing anon-serializable JDK class must still throw, just deferred to write time. + + Fory fory = Fory.builder() + .withLanguage(Language.JAVA) + .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")); + } + } + }