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 1d8db7376 fix(java): support skip optional sql serializers for java11+ 
(#3553)
1d8db7376 is described below

commit 1d8db737689f979bf9fda322f5f4e341c15ca7ff
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Apr 12 13:37:29 2026 +0800

    fix(java): support skip optional sql serializers for java11+ (#3553)
    
    ## Why?
    
    ## What does this PR do?
    
    - move optional SQL serializer access behind reflective helpers so
    startup does not touch `java.sql` unless those classes are actually used
    - add a JPMS regression test for building without `java.sql` and
    preserve time ref-tracking behavior for lazy serializer registrations
    
    ## Related issues
    
    Fixes #1695
    
    ## 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?
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
---
 .../org/apache/fory/resolver/ClassResolver.java    |  14 +-
 .../org/apache/fory/resolver/XtypeResolver.java    |  15 +-
 .../apache/fory/serializer/SqlTimeSerializers.java | 159 +++++++++++++++++++++
 .../apache/fory/serializer/TimeSerializers.java    |  94 +-----------
 .../main/java/org/apache/fory/type/TypeUtils.java  |  55 +++++--
 .../apache/fory/JpmsOptionalClassLoadingTest.java  |  84 +++++++++++
 .../fory/serializer/TimeSerializersTest.java       |   4 +-
 .../format/encoder/BaseBinaryEncoderBuilder.java   |   3 +-
 .../apache/fory/format/row/binary/BinaryUtils.java |   4 +-
 9 files changed, 319 insertions(+), 113 deletions(-)

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 3a7dbbd2c..f82f6fd07 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
@@ -35,7 +35,6 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
-import java.sql.Timestamp;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -127,6 +126,7 @@ import org.apache.fory.serializer.Serializer;
 import org.apache.fory.serializer.SerializerFactory;
 import org.apache.fory.serializer.Serializers;
 import org.apache.fory.serializer.Shareable;
+import org.apache.fory.serializer.SqlTimeSerializers;
 import org.apache.fory.serializer.TimeSerializers;
 import org.apache.fory.serializer.UnknownClass;
 import org.apache.fory.serializer.UnknownClass.UnknownEmptyStruct;
@@ -329,6 +329,16 @@ public class ClassResolver extends TypeResolver {
     ArraySerializers.registerDefaultSerializers(this);
     PrimitiveListSerializers.registerDefaultSerializers(this);
     TimeSerializers.registerDefaultSerializers(this);
+    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();
+    }
     OptionalSerializers.registerDefaultSerializers(this);
     CollectionSerializers.registerDefaultSerializers(this);
     MapSerializers.registerDefaultSerializers(this);
@@ -374,7 +384,7 @@ public class ClassResolver extends TypeResolver {
   private void registerCommonUsedClasses() {
     registerInternal(LinkedList.class, TreeSet.class);
     registerInternal(LinkedHashMap.class, TreeMap.class);
-    registerInternal(Date.class, Timestamp.class, LocalDateTime.class, 
Instant.class);
+    registerInternal(Date.class, LocalDateTime.class, Instant.class);
     registerInternal(BigInteger.class, BigDecimal.class);
     registerInternal(Optional.class, OptionalInt.class);
     registerInternal(Boolean[].class, Byte[].class, Short[].class, 
Character[].class);
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java 
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index 3fb39fb6f..d02da96a8 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -29,7 +29,6 @@ import static org.apache.fory.type.Types.INVALID_USER_TYPE_ID;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -87,6 +86,7 @@ import org.apache.fory.serializer.SerializationUtils;
 import org.apache.fory.serializer.Serializer;
 import org.apache.fory.serializer.Serializers;
 import org.apache.fory.serializer.Shareable;
+import org.apache.fory.serializer.SqlTimeSerializers;
 import org.apache.fory.serializer.TimeSerializers;
 import org.apache.fory.serializer.UnionSerializer;
 import org.apache.fory.serializer.UnknownClass;
@@ -943,9 +943,16 @@ public class XtypeResolver extends TypeResolver {
     registerType(Types.DURATION, Duration.class, new 
TimeSerializers.DurationSerializer(config));
     registerType(Types.TIMESTAMP, Instant.class, new 
TimeSerializers.InstantSerializer(config));
     registerType(Types.TIMESTAMP, Date.class, new 
TimeSerializers.DateSerializer(config));
-    registerType(
-        Types.TIMESTAMP, java.sql.Date.class, new 
TimeSerializers.SqlDateSerializer(config));
-    registerType(Types.TIMESTAMP, Timestamp.class, new 
TimeSerializers.TimestampSerializer(config));
+    if (SqlTimeSerializers.isSqlModuleAvailable()) {
+      registerType(
+          Types.TIMESTAMP,
+          TypeUtils.SQL_DATE_TYPE.getRawType(),
+          SqlTimeSerializers.newSqlDateSerializer(config));
+      registerType(
+          Types.TIMESTAMP,
+          TypeUtils.TIMESTAMP_TYPE.getRawType(),
+          SqlTimeSerializers.newTimestampSerializer(config));
+    }
     registerType(
         Types.TIMESTAMP, LocalDateTime.class, new 
TimeSerializers.LocalDateTimeSerializer(config));
     registerType(Types.DATE, LocalDate.class, new 
TimeSerializers.LocalDateSerializer(config));
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/serializer/SqlTimeSerializers.java
 
b/java/fory-core/src/main/java/org/apache/fory/serializer/SqlTimeSerializers.java
new file mode 100644
index 000000000..dc1247a0e
--- /dev/null
+++ 
b/java/fory-core/src/main/java/org/apache/fory/serializer/SqlTimeSerializers.java
@@ -0,0 +1,159 @@
+/*
+ * 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 java.sql.Time;
+import java.sql.Timestamp;
+import java.time.Instant;
+import org.apache.fory.config.Config;
+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.TypeResolver;
+
+/** Optional SQL time serializers loaded only when {@code java.sql} types are 
actually used. */
+public final class SqlTimeSerializers {
+  private static final String SQL_DATE_CLASS_NAME = "java.sql.Date";
+  private static final String SQL_TIME_CLASS_NAME = "java.sql.Time";
+  private static final String SQL_TIMESTAMP_CLASS_NAME = "java.sql.Timestamp";
+  private static final int NUM_RESERVED_TYPE_IDS = 3;
+  private static final boolean SQL_MODULE_AVAILABLE =
+      isClassAvailable(SQL_DATE_CLASS_NAME)
+          && isClassAvailable(SQL_TIME_CLASS_NAME)
+          && isClassAvailable(SQL_TIMESTAMP_CLASS_NAME);
+
+  private SqlTimeSerializers() {}
+
+  public static boolean isSqlModuleAvailable() {
+    return SQL_MODULE_AVAILABLE;
+  }
+
+  public static int getNumReservedTypeIds() {
+    return NUM_RESERVED_TYPE_IDS;
+  }
+
+  public static Serializer<?> newSqlDateSerializer(Config config) {
+    return new SqlDateSerializer(config);
+  }
+
+  public static Serializer<?> newTimestampSerializer(Config config) {
+    return new TimestampSerializer(config);
+  }
+
+  public static void registerDefaultSerializers(TypeResolver resolver) {
+    Config config = resolver.getConfig();
+    resolver.registerInternalSerializer(java.sql.Date.class, 
newSqlDateSerializer(config));
+    resolver.registerInternalSerializer(Time.class, new 
SqlTimeSerializer(config));
+    resolver.registerInternalSerializer(Timestamp.class, 
newTimestampSerializer(config));
+  }
+
+  private static boolean isClassAvailable(String className) {
+    try {
+      Class.forName(className, false, 
SqlTimeSerializers.class.getClassLoader());
+      return true;
+    } catch (ClassNotFoundException e) {
+      return false;
+    }
+  }
+
+  public static final class SqlDateSerializer
+      extends TimeSerializers.BaseDateSerializer<java.sql.Date> {
+    public SqlDateSerializer(Config config) {
+      super(config, java.sql.Date.class);
+    }
+
+    public SqlDateSerializer(Config config, boolean needToWriteRef) {
+      super(config, java.sql.Date.class, needToWriteRef);
+    }
+
+    @Override
+    protected java.sql.Date newInstance(long time) {
+      return new java.sql.Date(time);
+    }
+
+    @Override
+    public java.sql.Date copy(CopyContext copyContext, java.sql.Date value) {
+      return newInstance(value.getTime());
+    }
+  }
+
+  public static final class SqlTimeSerializer extends 
TimeSerializers.BaseDateSerializer<Time> {
+    public SqlTimeSerializer(Config config) {
+      super(config, Time.class);
+    }
+
+    public SqlTimeSerializer(Config config, boolean needToWriteRef) {
+      super(config, Time.class, needToWriteRef);
+    }
+
+    @Override
+    protected Time newInstance(long time) {
+      return new Time(time);
+    }
+
+    @Override
+    public Time copy(CopyContext copyContext, Time value) {
+      return newInstance(value.getTime());
+    }
+  }
+
+  public static final class TimestampSerializer extends 
TimeSerializers.TimeSerializer<Timestamp> {
+    public TimestampSerializer(Config config) {
+      super(config, Timestamp.class);
+    }
+
+    public TimestampSerializer(Config config, boolean needToWriteRef) {
+      super(config, Timestamp.class, needToWriteRef);
+    }
+
+    @Override
+    public void write(WriteContext writeContext, Timestamp value) {
+      MemoryBuffer buffer = writeContext.getBuffer();
+      if (config.isXlang()) {
+        Instant instant = value.toInstant();
+        buffer.writeInt64(instant.getEpochSecond());
+        buffer.writeInt32(instant.getNano());
+      } else {
+        long time = value.getTime() - (value.getNanos() / 1_000_000);
+        buffer.writeInt64(time);
+        buffer.writeInt32(value.getNanos());
+      }
+    }
+
+    @Override
+    public Timestamp read(ReadContext readContext) {
+      MemoryBuffer buffer = readContext.getBuffer();
+      if (config.isXlang()) {
+        long seconds = buffer.readInt64();
+        int nanos = buffer.readInt32();
+        return Timestamp.from(Instant.ofEpochSecond(seconds, nanos));
+      }
+      Timestamp t = new Timestamp(buffer.readInt64());
+      t.setNanos(buffer.readInt32());
+      return t;
+    }
+
+    @Override
+    public Timestamp copy(CopyContext copyContext, Timestamp value) {
+      return new Timestamp(value.getTime());
+    }
+  }
+}
diff --git 
a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java 
b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
index 95ce87e0a..a49da25d5 100644
--- 
a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
+++ 
b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
@@ -19,8 +19,6 @@
 
 package org.apache.fory.serializer;
 
-import java.sql.Time;
-import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -44,7 +42,7 @@ 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.TypeResolver;
+import org.apache.fory.resolver.ClassResolver;
 import org.apache.fory.util.DateTimeUtils;
 
 /** Serializers for all time related types. */
@@ -119,91 +117,6 @@ public class TimeSerializers {
     }
   }
 
-  public static final class SqlDateSerializer extends 
BaseDateSerializer<java.sql.Date> {
-    public SqlDateSerializer(Config config) {
-      super(config, java.sql.Date.class);
-    }
-
-    public SqlDateSerializer(Config config, boolean needToWriteRef) {
-      super(config, java.sql.Date.class, needToWriteRef);
-    }
-
-    @Override
-    protected java.sql.Date newInstance(long time) {
-      return new java.sql.Date(time);
-    }
-
-    @Override
-    public java.sql.Date copy(CopyContext copyContext, java.sql.Date value) {
-      return newInstance(value.getTime());
-    }
-  }
-
-  public static final class SqlTimeSerializer extends BaseDateSerializer<Time> 
{
-
-    public SqlTimeSerializer(Config config) {
-      super(config, Time.class);
-    }
-
-    public SqlTimeSerializer(Config config, boolean needToWriteRef) {
-      super(config, Time.class, needToWriteRef);
-    }
-
-    @Override
-    protected Time newInstance(long time) {
-      return new Time(time);
-    }
-
-    @Override
-    public Time copy(CopyContext copyContext, Time value) {
-      return newInstance(value.getTime());
-    }
-  }
-
-  public static final class TimestampSerializer extends 
TimeSerializer<Timestamp> {
-
-    public TimestampSerializer(Config config) {
-      // conflict with instant
-      super(config, Timestamp.class);
-    }
-
-    public TimestampSerializer(Config config, boolean needToWriteRef) {
-      super(config, Timestamp.class, needToWriteRef);
-    }
-
-    @Override
-    public void write(WriteContext writeContext, Timestamp value) {
-      MemoryBuffer buffer = writeContext.getBuffer();
-      if (config.isXlang()) {
-        Instant instant = value.toInstant();
-        buffer.writeInt64(instant.getEpochSecond());
-        buffer.writeInt32(instant.getNano());
-      } else {
-        long time = value.getTime() - (value.getNanos() / 1_000_000);
-        buffer.writeInt64(time);
-        buffer.writeInt32(value.getNanos());
-      }
-    }
-
-    @Override
-    public Timestamp read(ReadContext readContext) {
-      MemoryBuffer buffer = readContext.getBuffer();
-      if (config.isXlang()) {
-        long seconds = buffer.readInt64();
-        int nanos = buffer.readInt32();
-        return Timestamp.from(Instant.ofEpochSecond(seconds, nanos));
-      }
-      Timestamp t = new Timestamp(buffer.readInt64());
-      t.setNanos(buffer.readInt32());
-      return t;
-    }
-
-    @Override
-    public Timestamp copy(CopyContext copyContext, Timestamp value) {
-      return new Timestamp(value.getTime());
-    }
-  }
-
   public static final class LocalDateSerializer extends 
ImmutableTimeSerializer<LocalDate>
       implements Shareable {
     public LocalDateSerializer(Config config) {
@@ -723,12 +636,9 @@ public class TimeSerializers {
     }
   }
 
-  public static void registerDefaultSerializers(TypeResolver resolver) {
+  public static void registerDefaultSerializers(ClassResolver resolver) {
     Config config = resolver.getConfig();
     resolver.registerInternalSerializer(Date.class, new 
DateSerializer(config));
-    resolver.registerInternalSerializer(java.sql.Date.class, new 
SqlDateSerializer(config));
-    resolver.registerInternalSerializer(Time.class, new 
SqlTimeSerializer(config));
-    resolver.registerInternalSerializer(Timestamp.class, new 
TimestampSerializer(config));
     resolver.registerInternalSerializer(LocalDate.class, new 
LocalDateSerializer(config));
     resolver.registerInternalSerializer(LocalTime.class, new 
LocalTimeSerializer(config));
     resolver.registerInternalSerializer(LocalDateTime.class, new 
LocalDateTimeSerializer(config));
diff --git a/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java 
b/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
index caa8b3ab9..aa941e882 100644
--- a/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
+++ b/java/fory-core/src/main/java/org/apache/fory/type/TypeUtils.java
@@ -32,9 +32,6 @@ import java.lang.reflect.WildcardType;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -96,6 +93,10 @@ import org.apache.fory.util.record.RecordUtils;
 /** Type utils for common type inference and extraction. */
 @SuppressWarnings({"UnstableApiUsage", "unchecked"})
 public class TypeUtils {
+  private static final String SQL_DATE_CLASS_NAME = "java.sql.Date";
+  private static final String SQL_TIME_CLASS_NAME = "java.sql.Time";
+  private static final String SQL_TIMESTAMP_CLASS_NAME = "java.sql.Timestamp";
+
   public static final String JAVA_BOOLEAN = "boolean";
   public static final String JAVA_BYTE = "byte";
   public static final String JAVA_SHORT = "short";
@@ -124,9 +125,9 @@ public class TypeUtils {
   public static final TypeRef<?> STRING_TYPE = TypeRef.of(String.class);
   public static final TypeRef<?> BIG_DECIMAL_TYPE = 
TypeRef.of(BigDecimal.class);
   public static final TypeRef<?> BIG_INTEGER_TYPE = 
TypeRef.of(BigInteger.class);
-  public static final TypeRef<?> DATE_TYPE = TypeRef.of(Date.class);
+  public static final TypeRef<?> SQL_DATE_TYPE = 
optionalTypeRef(SQL_DATE_CLASS_NAME);
   public static final TypeRef<?> LOCAL_DATE_TYPE = TypeRef.of(LocalDate.class);
-  public static final TypeRef<?> TIMESTAMP_TYPE = TypeRef.of(Timestamp.class);
+  public static final TypeRef<?> TIMESTAMP_TYPE = 
optionalTypeRef(SQL_TIMESTAMP_CLASS_NAME);
   public static final TypeRef<?> INSTANT_TYPE = TypeRef.of(Instant.class);
   public static final TypeRef<?> BINARY_TYPE = TypeRef.of(byte[].class);
   public static final TypeRef<?> ITERABLE_TYPE = TypeRef.of(Iterable.class);
@@ -194,9 +195,9 @@ public class TypeUtils {
     SUPPORTED_TYPES.add(STRING_TYPE);
     SUPPORTED_TYPES.add(BIG_DECIMAL_TYPE);
     // SUPPORTED_TYPES.add(BIG_INTEGER_TYPE);
-    SUPPORTED_TYPES.add(DATE_TYPE);
+    addIfNotNull(SUPPORTED_TYPES, SQL_DATE_TYPE);
     SUPPORTED_TYPES.add(LOCAL_DATE_TYPE);
-    SUPPORTED_TYPES.add(TIMESTAMP_TYPE);
+    addIfNotNull(SUPPORTED_TYPES, TIMESTAMP_TYPE);
     SUPPORTED_TYPES.add(INSTANT_TYPE);
     SUPPORTED_TYPES.add(OPTIONAL_TYPE);
     SUPPORTED_TYPES.add(OPTIONAL_INT_TYPE);
@@ -263,6 +264,33 @@ public class TypeUtils {
     backward.put(value, key);
   }
 
+  private static void addIfNotNull(Set<TypeRef<?>> set, TypeRef<?> typeRef) {
+    if (typeRef != null) {
+      set.add(typeRef);
+    }
+  }
+
+  private static TypeRef<?> optionalTypeRef(String className) {
+    Class<?> cls = loadOptionalClass(className);
+    return cls == null ? null : TypeRef.of(cls);
+  }
+
+  private static Class<?> loadOptionalClass(String className) {
+    ClassLoader contextClassLoader = 
Thread.currentThread().getContextClassLoader();
+    if (contextClassLoader != null) {
+      try {
+        return Class.forName(className, false, contextClassLoader);
+      } catch (ClassNotFoundException ignored) {
+        // Fall through to the defining class loader.
+      }
+    }
+    try {
+      return Class.forName(className, false, TypeUtils.class.getClassLoader());
+    } catch (ClassNotFoundException ignored) {
+      return null;
+    }
+  }
+
   public static boolean isNullable(Class<?> clz) {
     return !isPrimitive(clz);
   }
@@ -965,9 +993,6 @@ public class TypeUtils {
     leafTypes.addAll(
         Arrays.asList(
             java.util.Date.class,
-            java.sql.Date.class,
-            Time.class,
-            Timestamp.class,
             LocalDate.class,
             LocalTime.class,
             LocalDateTime.class,
@@ -985,6 +1010,16 @@ public class TypeUtils {
             Calendar.class,
             GregorianCalendar.class,
             TimeZone.class));
+    addOptionalLeafType(SQL_DATE_CLASS_NAME);
+    addOptionalLeafType(SQL_TIME_CLASS_NAME);
+    addOptionalLeafType(SQL_TIMESTAMP_CLASS_NAME);
+  }
+
+  private static void addOptionalLeafType(String className) {
+    Class<?> cls = loadOptionalClass(className);
+    if (cls != null) {
+      leafTypes.add(cls);
+    }
   }
 
   private static final WeakHashMap<Class<?>, Boolean> hasExpandableLeafsCache 
= new WeakHashMap<>();
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java
 
b/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java
new file mode 100644
index 000000000..d34fb18a9
--- /dev/null
+++ 
b/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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 java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import org.apache.fory.memory.Platform;
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+public class JpmsOptionalClassLoadingTest {
+  @Test
+  public void testBuildWithoutJavaSqlModule() throws Exception {
+    if (Platform.JAVA_VERSION < 9) {
+      throw new SkipException("Skip on jdk" + Platform.JAVA_VERSION);
+    }
+    String javaBin =
+        System.getProperty("java.home") + File.separator + "bin" + 
File.separator + "java";
+    Process process =
+        new ProcessBuilder(
+                javaBin,
+                "--limit-modules",
+                "java.base,java.logging,jdk.unsupported",
+                "-cp",
+                System.getProperty("java.class.path"),
+                NoJavaSqlMain.class.getName())
+            .redirectErrorStream(true)
+            .start();
+    String output = readFully(process.getInputStream());
+    assertEquals(process.waitFor(), 0, output);
+  }
+
+  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);
+  }
+
+  public static final class NoJavaSqlMain {
+    public static void main(String[] args) {
+      Fory fory = Fory.builder().requireClassRegistration(false).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);
+      }
+    }
+  }
+
+  public static final class SampleValue implements Serializable {
+    private final String value;
+
+    public SampleValue(String value) {
+      this.value = value;
+    }
+  }
+}
diff --git 
a/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java
 
b/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java
index 5306ac826..af5309b02 100644
--- 
a/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java
+++ 
b/java/fory-core/src/test/java/org/apache/fory/serializer/TimeSerializersTest.java
@@ -334,7 +334,7 @@ public class TimeSerializersTest extends ForyTestBase {
       fory.registerSerializer(
           Date.class, new TimeSerializers.DateSerializer(fory.getConfig(), 
true));
       fory.registerSerializer(
-          java.sql.Date.class, new 
TimeSerializers.SqlDateSerializer(fory.getConfig(), true));
+          java.sql.Date.class, new 
SqlTimeSerializers.SqlDateSerializer(fory.getConfig(), true));
       fory.registerSerializer(
           Instant.class, new 
TimeSerializers.InstantSerializer(fory.getConfig(), true));
       {
@@ -365,7 +365,7 @@ public class TimeSerializersTest extends ForyTestBase {
         TimeStructRef1.class, CodegenSerializer.loadCodegenSerializer(fory, 
TimeStructRef1.class));
     fory.registerSerializer(Date.class, new 
TimeSerializers.DateSerializer(fory.getConfig(), true));
     fory.registerSerializer(
-        java.sql.Date.class, new 
TimeSerializers.SqlDateSerializer(fory.getConfig(), true));
+        java.sql.Date.class, new 
SqlTimeSerializers.SqlDateSerializer(fory.getConfig(), true));
     fory.registerSerializer(
         Instant.class, new TimeSerializers.InstantSerializer(fory.getConfig(), 
true));
     {
diff --git 
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/BaseBinaryEncoderBuilder.java
 
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/BaseBinaryEncoderBuilder.java
index e27bbdb27..2d214e287 100644
--- 
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/BaseBinaryEncoderBuilder.java
+++ 
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/BaseBinaryEncoderBuilder.java
@@ -646,7 +646,8 @@ public abstract class BaseBinaryEncoderBuilder extends 
CodecBuilder {
       return new StaticInvoke(
           DateTimeUtils.class, "daysToLocalDate", TypeUtils.LOCAL_DATE_TYPE, 
false, value);
     } else if (rawType == java.sql.Date.class) {
-      return new StaticInvoke(DateTimeUtils.class, "toJavaDate", 
TypeUtils.DATE_TYPE, false, value);
+      return new StaticInvoke(
+          DateTimeUtils.class, "toJavaDate", TypeUtils.SQL_DATE_TYPE, false, 
value);
     } else if (rawType == java.sql.Timestamp.class) {
       return new StaticInvoke(
           DateTimeUtils.class, "toJavaTimestamp", TypeUtils.TIMESTAMP_TYPE, 
false, value);
diff --git 
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryUtils.java
 
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryUtils.java
index 29cd2e160..b2ada55dd 100644
--- 
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryUtils.java
+++ 
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryUtils.java
@@ -51,7 +51,7 @@ public class BinaryUtils {
       return "getFloat64";
     } else if (TypeUtils.BIG_DECIMAL_TYPE.equals(type)) {
       return "getDecimal";
-    } else if (TypeUtils.DATE_TYPE.equals(type)) {
+    } else if (TypeUtils.SQL_DATE_TYPE.equals(type)) {
       return "getDate";
     } else if (TypeUtils.TIMESTAMP_TYPE.equals(type)) {
       return "getTimestamp";
@@ -103,7 +103,7 @@ public class BinaryUtils {
       return TypeUtils.PRIMITIVE_DOUBLE_TYPE;
     } else if (TypeUtils.BIG_DECIMAL_TYPE.equals(type)) {
       return TypeUtils.BIG_DECIMAL_TYPE;
-    } else if (TypeUtils.DATE_TYPE.equals(type)) {
+    } else if (TypeUtils.SQL_DATE_TYPE.equals(type)) {
       return TypeUtils.INT_TYPE;
     } else if (TypeUtils.TIMESTAMP_TYPE.equals(type)) {
       return TypeUtils.LONG_TYPE;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to