This is an automated email from the ASF dual-hosted git repository.
mpochatkin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 0666e26f594 IGNITE-26181 Fix default byte array marshalling (#6521)
0666e26f594 is described below
commit 0666e26f594f80dce6a950821aefb0fec4364a89
Author: Mikhail <[email protected]>
AuthorDate: Thu Sep 4 08:08:27 2025 +0300
IGNITE-26181 Fix default byte array marshalling (#6521)
---
.../ignite/marshalling/ByteArrayMarshaller.java | 49 ++++++++++++++-
.../JavaSerializationByteArrayMarshalling.java | 70 ----------------------
2 files changed, 47 insertions(+), 72 deletions(-)
diff --git
a/modules/api/src/main/java/org/apache/ignite/marshalling/ByteArrayMarshaller.java
b/modules/api/src/main/java/org/apache/ignite/marshalling/ByteArrayMarshaller.java
index 50bfa3d51e4..94b71a5bf08 100644
---
a/modules/api/src/main/java/org/apache/ignite/marshalling/ByteArrayMarshaller.java
+++
b/modules/api/src/main/java/org/apache/ignite/marshalling/ByteArrayMarshaller.java
@@ -17,6 +17,12 @@
package org.apache.ignite.marshalling;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
import java.io.Serializable;
import org.jetbrains.annotations.Nullable;
@@ -39,7 +45,14 @@ public interface ByteArrayMarshaller<T> extends
Marshaller<T, byte[]> {
}
if (object instanceof Serializable) {
- return
JavaSerializationByteArrayMarshalling.marshal((Serializable) object);
+ try (var baos = new ByteArrayOutputStream(); var out = new
ObjectOutputStream(baos)) {
+ out.writeObject(object);
+ out.flush();
+
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new MarshallingException(e);
+ }
}
throw new UnsupportedObjectTypeMarshallingException(object.getClass());
@@ -51,6 +64,38 @@ public interface ByteArrayMarshaller<T> extends
Marshaller<T, byte[]> {
return null;
}
- return JavaSerializationByteArrayMarshalling.unmarshal(raw);
+ try (var bais = new ByteArrayInputStream(raw); var ois = new
ObjectInputStream(bais) {
+ /*
+ * Why do we subclass ObjectInputStream here?
+ *
+ * - Class loading in a distributed / runtime‑extensible
environment:
+ * Ignite often needs to deserialize user classes that are not
visible to the
+ * system/application class loader (for example, user code
deployed as a compute unit,
+ * classes loaded by a job/unit-specific ClassLoader).
+ * The default ObjectInputStream#resolveClass uses an internal
heuristic ("latest
+ * user-defined loader") that frequently ends up being the
system or context class loader.
+ * That loader may not see such user classes, leading to
ClassNotFoundException during
+ * deserialization.
+ *
+ * - Deterministic loader selection:
+ * We explicitly resolve classes using the ClassLoader that
defined this ByteArrayMarshaller
+ * instance. In Ignite, this loader is set up to be the job/unit
ClassLoader when user code
+ * is executed or transported. This makes class resolution
deterministic and aligned with
+ * the deployment context, mirroring the environment where
serialization occurred.
+ */
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass desc) throws
IOException, ClassNotFoundException {
+ String name = desc.getName();
+ try {
+ return Class.forName(name, false,
ByteArrayMarshaller.this.getClass().getClassLoader());
+ } catch (ClassNotFoundException ex) {
+ return super.resolveClass(desc);
+ }
+ }
+ }) {
+ return (T) ois.readObject();
+ } catch (IOException | ClassNotFoundException e) {
+ throw new MarshallingException(e);
+ }
}
}
diff --git
a/modules/api/src/main/java/org/apache/ignite/marshalling/JavaSerializationByteArrayMarshalling.java
b/modules/api/src/main/java/org/apache/ignite/marshalling/JavaSerializationByteArrayMarshalling.java
deleted file mode 100644
index 12845167478..00000000000
---
a/modules/api/src/main/java/org/apache/ignite/marshalling/JavaSerializationByteArrayMarshalling.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.ignite.marshalling;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Default java serialization marshaller. It is used by default if no other
marshaller is provided.
- */
-class JavaSerializationByteArrayMarshalling {
- /**
- * Writes the object to a byte array with java serialization.
- *
- * @param <T> object.
- * @return byte array that represents the object.
- */
- public static <T extends Serializable> byte @Nullable [] marshal(T object)
{
- if (object == null) {
- return null;
- }
-
- try (var baos = new ByteArrayOutputStream(); var out = new
ObjectOutputStream(baos)) {
- out.writeObject(object);
- out.flush();
-
- return baos.toByteArray();
- } catch (IOException e) {
- throw new MarshallingException(e);
- }
- }
-
- /**
- * Reads the object from a byte array with java serialization.
- *
- * @param raw byte array that represents the object.
- * @return object.
- */
- public static <T> @Nullable T unmarshal(byte @Nullable [] raw) {
- if (raw == null) {
- return null;
- }
-
- try (var bais = new ByteArrayInputStream(raw); var ois = new
ObjectInputStream(bais)) {
- return (T) ois.readObject();
- } catch (IOException | ClassNotFoundException e) {
- throw new MarshallingException(e);
- }
- }
-}