This is an automated email from the ASF dual-hosted git repository.

pefernan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git


The following commit(s) were added to refs/heads/main by this push:
     new 45a28b66aef [kie-issues#7150] Support java.lang.Object as a process 
variable type in BPM Processes (#7151)
45a28b66aef is described below

commit 45a28b66aefde8ebaf23a1a1fec76c11e53144d1
Author: Pere Fernández <[email protected]>
AuthorDate: Mon Oct 5 13:44:12 2026 +0200

    [kie-issues#7150] Support java.lang.Object as a process variable type in 
BPM Processes (#7151)
    
    * [kie-issues#7150] Support java.lang.Object as a process variable type in 
BPM Processes
    
    * - improving testing
    
    * - fix
    
    * - adding `ObjectVariableProcess.bpmn2` to rat exclude
    
    * - removing empty object from nested pojos
---
 .rat-excludes                                      |   1 +
 .../kogito/codegen/json/JsonSchemaGenerator.java   |  23 +++-
 .../persistence/proto/AbstractProtoGenerator.java  |   8 +-
 .../proto/ReflectionProtoGenerator.java            |   1 +
 .../org/kie/kogito/codegen/data/ObjectHolder.java  |  33 +++++
 .../kogito/codegen/data/ObjectProcessModel.java    |  53 ++++++++
 .../codegen/json/JsonSchemaGeneratorTest.java      |  89 ++++++++++++++
 .../kogito/codegen/process/ProcessCodegenTest.java |  14 +++
 .../proto/AbstractProtoGeneratorTest.java          |  75 ++++++++----
 .../objectvariable/ObjectVariableProcess.bpmn2     | 122 +++++++++++++++++++
 .../org/jbpm/compiler/canonical/ModelMetaData.java |  10 ++
 .../compiler/canonical/WorkItemModelMetaData.java  |  26 +++-
 .../flow/serialization/JavaSerializationUtils.java |  68 +++++++++++
 .../ProtobufObjectMarshallerStrategy.java          |  35 +-----
 .../SerializableProtostreamBaseMarshaller.java     |  29 ++---
 .../serialization/JavaSerializationUtilsTest.java  | 135 +++++++++++++++++++++
 .../workflow/deployment/JandexProtoGenerator.java  |   1 +
 .../workflow/deployment/JandexTestUtils.java       |   4 +
 18 files changed, 648 insertions(+), 79 deletions(-)

diff --git a/.rat-excludes b/.rat-excludes
index f7bb5614773..e8d8ad5827a 100644
--- a/.rat-excludes
+++ b/.rat-excludes
@@ -2354,6 +2354,7 @@ 
kogito-codegen-modules/kogito-codegen-predictions/src/test/resources/prediction/
 
kogito-codegen-modules/kogito-codegen-predictions/src/test/resources/prediction/test_multiplemodels.pmml
 
kogito-codegen-modules/kogito-codegen-predictions/src/test/resources/prediction/test_regression.pmml
 
kogito-codegen-modules/kogito-codegen-predictions/src/test/resources/prediction/test_scorecard.pmml
+kogito-codegen-modules/kogito-codegen-processes/src/test/resources/objectvariable/ObjectVariableProcess.bpmn2
 
kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/META-INF/kogito.addon
 
kogito-codegen-modules/kogito-codegen-processes-integration-tests/src/test/resources/org/kie/kogito/codegen/process/process-generation-test.skip.txt
 
kogito-codegen-modules/kogito-codegen-sample/kogito-codegen-sample-generator/src/test/resources/sampleFile1.txt
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/json/JsonSchemaGenerator.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/json/JsonSchemaGenerator.java
index e101099eaf3..9b075485226 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/json/JsonSchemaGenerator.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/json/JsonSchemaGenerator.java
@@ -112,7 +112,7 @@ public class JsonSchemaGenerator {
         builder.forTypesInGeneral()
                 .withStringFormatResolver(target -> 
target.getSimpleTypeDescription().equals("Date") ? "date-time" : null);
         builder.forFields()
-                .withIgnoreCheck(JsonSchemaGenerator::checkFields)
+                .withIgnoreCheck(JsonSchemaGenerator::shouldIgnoreField)
                 .withCustomDefinitionProvider(this::getInputOutput);
         SchemaGenerator generator = new SchemaGenerator(builder.build());
         ObjectWriter writer = new ObjectMapper().writer();
@@ -164,9 +164,24 @@ public class JsonSchemaGenerator {
         throw new RuntimeException("Cannot create the schema name. Class must 
be have UserTask or ProcessInput annotation");
     }
 
-    private static boolean checkFields(FieldScope fieldScope) {
-        return 
(fieldScope.getDeclaringType().getErasedType().isAnnotationPresent(UserTask.class)
 && fieldScope.getAnnotation(UserTaskParam.class) == null)
-                || 
(fieldScope.getDeclaringType().getErasedType().isAnnotationPresent(ProcessInput.class)
 && fieldScope.getAnnotation(VariableInfo.class) == null);
+    private static boolean shouldIgnoreField(FieldScope fieldScope) {
+        Class<?> declaringType = fieldScope.getDeclaringType().getErasedType();
+
+        if (declaringType.isAnnotationPresent(UserTask.class) && 
fieldScope.getAnnotation(UserTaskParam.class) == null
+                || declaringType.isAnnotationPresent(ProcessInput.class) && 
fieldScope.getAnnotation(VariableInfo.class) == null) {
+            return true;
+        }
+
+        if (Object.class.equals(fieldScope.getDeclaredType().getErasedType())) 
{
+            logger.warn("Field '{}' in '{}' is declared as java.lang.Object 
and will be excluded from the " +
+                    "generated JSON schema. The UI cannot render a form widget 
for an untyped field. " +
+                    "Use a concrete type to enable form rendering.",
+                    fieldScope.getDeclaredName(),
+                    declaringType.getSimpleName());
+            return true;
+        }
+
+        return false;
     }
 
 }
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGenerator.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGenerator.java
index 10576e43d51..f507f640361 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGenerator.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGenerator.java
@@ -270,11 +270,17 @@ public abstract class AbstractProtoGenerator<T> 
implements ProtoGenerator {
                 if (cls.isInterface()) {
                     return null;
                 }
+                if (Object.class.getCanonicalName().equals(type)) {
+                    LOGGER.warn("A field is declared as java.lang.Object. " +
+                            "It will be persisted using Java serialization. " +
+                            "Make sure the stored value implements 
java.io.Serializable.");
+                    return KOGITO_SERIALIZABLE;
+                }
                 boolean assignable = Serializable.class.isAssignableFrom(cls);
                 if (assignable) {
                     return KOGITO_SERIALIZABLE;
                 } else {
-                    throw new IllegalArgumentException(format("Java type %s is 
no supported by Kogito persistence, please consider using a class that extends 
java.io.Serializable", type));
+                    throw new IllegalArgumentException(format("Java type %s is 
not supported by Kogito persistence, please consider using a class that extends 
java.io.Serializable", type));
                 }
             } catch (ClassNotFoundException e) {
                 return null;
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/ReflectionProtoGenerator.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/ReflectionProtoGenerator.java
index 05f3e7f57a9..8473365a2ae 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/ReflectionProtoGenerator.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/persistence/proto/ReflectionProtoGenerator.java
@@ -87,6 +87,7 @@ public class ReflectionProtoGenerator extends 
AbstractProtoGenerator<Class<?>> {
 
             String fieldTypeString = pd.getPropertyType().getCanonicalName();
             Class<?> fieldType = pd.getPropertyType();
+            LOGGER.debug("Resolving proto type for field '{}' of type '{}' in 
'{}'", pd.getName(), fieldTypeString, clazz.getName());
             String protoType;
             if (pd.getPropertyType().isArray() && 
!pd.getPropertyType().getComponentType().isPrimitive()) {
                 fieldTypeString = ARRAY;
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectHolder.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectHolder.java
new file mode 100644
index 00000000000..8acaec88179
--- /dev/null
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectHolder.java
@@ -0,0 +1,33 @@
+/*
+ * 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.kie.kogito.codegen.data;
+
+public class ObjectHolder {
+
+    private Object value;
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+}
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectProcessModel.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectProcessModel.java
new file mode 100644
index 00000000000..8703b5539ec
--- /dev/null
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/data/ObjectProcessModel.java
@@ -0,0 +1,53 @@
+/*
+ * 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.kie.kogito.codegen.data;
+
+import org.kie.kogito.MapInput;
+import org.kie.kogito.MapInputId;
+import org.kie.kogito.MapOutput;
+import org.kie.kogito.Model;
+
+import jakarta.validation.Valid;
+
[email protected](value = "kogito-codegen", reference = 
"objectProcess", name = "ObjectProcess", hidden = false)
+public class ObjectProcessModel implements Model, MapInput, MapInputId, 
MapOutput {
+
+    private String id;
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    @org.kie.kogito.codegen.VariableInfo(tags = "")
+    @com.fasterxml.jackson.annotation.JsonProperty(value = "value")
+    @jakarta.validation.Valid()
+    private Object value;
+
+    public @Valid() Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+}
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/json/JsonSchemaGeneratorTest.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/json/JsonSchemaGeneratorTest.java
index 9d72d78744d..82629c37037 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/json/JsonSchemaGeneratorTest.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/json/JsonSchemaGeneratorTest.java
@@ -46,6 +46,7 @@ import com.github.victools.jsonschema.generator.SchemaVersion;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static 
org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
+import static 
org.kie.kogito.codegen.json.JsonSchemaGenerator.DEFAULT_SCHEMA_VERSION;
 
 public class JsonSchemaGeneratorTest {
 
@@ -152,6 +153,43 @@ public class JsonSchemaGeneratorTest {
         private Person person;
     }
 
+    @ProcessInput(processName = "objectVarProcess")
+    private static class ProcessInputModelWithObjectVar {
+
+        @VariableInfo
+        private String name;
+
+        @VariableInfo
+        private Object data;
+    }
+
+    @UserTask(taskName = "objectVarTask", processName = "org.jbpm.test")
+    private static class UserTaskWithObjectVar {
+
+        @UserTaskParam(UserTaskParam.ParamType.INPUT)
+        private String name;
+
+        @UserTaskParam(UserTaskParam.ParamType.INPUT)
+        private Object payload;
+    }
+
+    private static class HolderWithObjectField {
+
+        private String label;
+
+        private Object nested;
+    }
+
+    @ProcessInput(processName = "nestedObjectVarProcess")
+    private static class ProcessInputModelWithNestedObjectVar {
+
+        @VariableInfo
+        private String name;
+
+        @VariableInfo
+        private HolderWithObjectField holder;
+    }
+
     private static class Person {
 
         @SuppressWarnings("unused")
@@ -415,4 +453,55 @@ public class JsonSchemaGeneratorTest {
     private String resolveDefinitionsProperty(SchemaVersion schemaVersion) {
         return SchemaVersion.DRAFT_2019_09.equals(schemaVersion) ? "$defs" : 
"definitions";
     }
+
+    @Test
+    public void testObjectTypedFieldIsExcludedFromSchema() throws IOException {
+        Collection<GeneratedFile> files = new JsonSchemaGenerator.ClassBuilder(
+                
Stream.of(ProcessInputModelWithObjectVar.class)).build().generate();
+        assertThat(files).hasSize(1);
+
+        ObjectReader reader = new ObjectMapper().reader();
+        JsonNode node = reader.readTree(files.iterator().next().contents());
+
+        JsonNode properties = node.get("properties");
+        assertThat(properties).isNotNull();
+        assertThat(properties.has("name")).isTrue();
+        assertThat(properties.has("data")).isFalse();
+    }
+
+    @Test
+    public void testObjectTypedUserTaskParamIsExcludedFromSchema() throws 
IOException {
+        Collection<GeneratedFile> files = new JsonSchemaGenerator.ClassBuilder(
+                Stream.of(UserTaskWithObjectVar.class)).build().generate();
+        assertThat(files).hasSize(1);
+
+        ObjectReader reader = new ObjectMapper().reader();
+        JsonNode properties = 
reader.readTree(files.iterator().next().contents()).get("properties");
+        assertThat(properties).isNotNull();
+        assertThat(properties.has("name")).isTrue();
+        assertThat(properties.has("payload")).isFalse();
+    }
+
+    @Test
+    public void testNestedObjectTypedFieldIsExcludedFromSchema() throws 
IOException {
+        Collection<GeneratedFile> files = new JsonSchemaGenerator.ClassBuilder(
+                
Stream.of(ProcessInputModelWithNestedObjectVar.class)).build().generate();
+        assertThat(files).hasSize(1);
+
+        ObjectReader reader = new ObjectMapper().reader();
+        JsonNode node = reader.readTree(files.iterator().next().contents());
+
+        // top-level: holder present, name present
+        JsonNode properties = node.get("properties");
+        assertThat(properties).isNotNull();
+        assertThat(properties.has("name")).isTrue();
+        assertThat(properties.has("holder")).isTrue();
+
+        // nested: label present, but nested Object field excluded
+        String definitionsKey = 
resolveDefinitionsProperty(DEFAULT_SCHEMA_VERSION);
+        JsonNode holderDef = 
node.get(definitionsKey).get("HolderWithObjectField");
+        assertThat(holderDef).isNotNull();
+        assertThat(holderDef.get("properties").has("label")).isTrue();
+        assertThat(holderDef.get("properties").has("nested")).isFalse();
+    }
 }
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
index d24d83d5690..c9970338878 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessCodegenTest.java
@@ -51,6 +51,8 @@ class ProcessCodegenTest {
     private static final Path MESSAGE_USERTASK_SOURCE_FULL_SOURCE = 
BASE_PATH.resolve(MESSAGE_USERTASK_SOURCE);
     private static final String REST_HYPHEN_PARAMS_SOURCE = 
"workitem/RestServiceHyphenParams.bpmn2";
     private static final Path REST_HYPHEN_PARAMS_FULL_PATH = 
BASE_PATH.resolve(REST_HYPHEN_PARAMS_SOURCE);
+    private static final String OBJECT_VARIABLE_SOURCE = 
"objectvariable/ObjectVariableProcess.bpmn2";
+    private static final Path OBJECT_VARIABLE_SOURCE_FULL_PATH = 
BASE_PATH.resolve(OBJECT_VARIABLE_SOURCE);
 
     @ParameterizedTest
     
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
@@ -195,4 +197,16 @@ class ProcessCodegenTest {
                 .as("Hyphenated identifier QUERY_max-size must not appear in 
generated code")
                 .doesNotContain("QUERY_max-size");
     }
+
+    @ParameterizedTest
+    
@MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#contextBuilders")
+    public void 
objectVariableProcessGeneratesWithoutError(KogitoBuildContext.Builder 
contextBuilder) {
+        KogitoBuildContext context = contextBuilder.build();
+        ProcessCodegen codeGenerator = ProcessCodegen.ofCollectedResources(
+                context,
+                CollectedResourceProducer.fromFiles(BASE_PATH, 
OBJECT_VARIABLE_SOURCE_FULL_PATH.toFile()));
+
+        Collection<GeneratedFile> generatedFiles = codeGenerator.generate();
+        assertThat(generatedFiles).isNotEmpty();
+    }
 }
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGeneratorTest.java
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGeneratorTest.java
index b5921519bcc..baf97025f95 100644
--- 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGeneratorTest.java
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/persistence/proto/AbstractProtoGeneratorTest.java
@@ -19,33 +19,11 @@
 package org.kie.kogito.codegen.process.persistence.proto;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import org.drools.codegen.common.GeneratedFile;
 import org.junit.jupiter.api.Test;
-import org.kie.kogito.codegen.data.Address;
-import org.kie.kogito.codegen.data.Answer;
-import org.kie.kogito.codegen.data.AnswerBroken;
-import org.kie.kogito.codegen.data.AnswerBrokenV2;
-import org.kie.kogito.codegen.data.AnswerWithAnnotations;
-import org.kie.kogito.codegen.data.GeneratedPOJO;
-import org.kie.kogito.codegen.data.Hello;
-import org.kie.kogito.codegen.data.HelloModel;
-import org.kie.kogito.codegen.data.JacksonData;
-import org.kie.kogito.codegen.data.ListWithoutType;
-import org.kie.kogito.codegen.data.Person;
-import org.kie.kogito.codegen.data.PersonSubClass;
-import org.kie.kogito.codegen.data.PersonVarInfo;
-import org.kie.kogito.codegen.data.PersonWithAddress;
-import org.kie.kogito.codegen.data.PersonWithAddresses;
-import org.kie.kogito.codegen.data.PersonWithList;
-import org.kie.kogito.codegen.data.Question;
-import org.kie.kogito.codegen.data.QuestionWithAnnotatedEnum;
-import org.kie.kogito.codegen.data.Travels;
+import org.kie.kogito.codegen.data.*;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 
@@ -826,6 +804,55 @@ public abstract class AbstractProtoGeneratorTest<T> {
         assertClassIsIncludedInSubclass(person, personSubClass);
     }
 
+    @Test
+    void pojoWithObjectField() {
+        AbstractProtoGenerator<T> generator = protoGeneratorBuilder()
+                
.withDataClasses(Collections.singleton(convertToType(ObjectHolder.class)))
+                .build(null);
+
+        Proto proto = 
generator.protoOfDataClasses("org.kie.kogito.test.persons");
+        assertThat(proto).isNotNull();
+
+        
assertThat(proto.getPackageName()).isEqualTo("org.kie.kogito.test.persons");
+        assertThat(proto.getSyntax()).isEqualTo("proto2");
+        assertThat(proto.getMessages()).hasSize(1);
+
+        ProtoMessage protoMessage = proto.getMessages().get(0);
+        assertThat(protoMessage).isNotNull();
+        assertThat(protoMessage.getName()).isEqualTo("ObjectHolder");
+        
assertThat(protoMessage.getJavaPackageOption()).isEqualTo("org.kie.kogito.codegen.data");
+        assertThat(protoMessage.getFields()).hasSize(1)
+                .element(0)
+                .hasFieldOrPropertyWithValue("name", "value")
+                .hasFieldOrPropertyWithValue("type", "kogito.Serializable");
+    }
+
+    @Test
+    void modelWithObjectField() {
+        AbstractProtoGenerator<T> generator = protoGeneratorBuilder()
+                
.build(Collections.singleton(convertToType(ObjectProcessModel.class)));
+
+        Proto proto = generator.generate("comment", 
ProtoGenerator.INDEX_COMMENT, "org.kie.kogito.test",
+                convertToType(ObjectProcessModel.class));
+
+        assertThat(proto.getPackageName()).isEqualTo("org.kie.kogito.test");
+        assertThat(proto.getSyntax()).isEqualTo("proto2");
+        assertThat(proto.getMessages()).hasSize(1);
+
+        ProtoMessage message = proto.getMessages().get(0);
+        assertThat(message.getName()).isEqualTo("ObjectProcess");
+        
assertThat(message.getJavaPackageOption()).isEqualTo("org.kie.kogito.codegen.data");
+
+        assertThat(message.getFields()).hasSize(2);
+
+        ProtoField valueField = message.getFields().stream()
+                .filter(f -> f.getName().equals("value"))
+                .findFirst()
+                .orElseThrow();
+        
assertThat(valueField.getType()).isEqualTo(ProtoGenerator.KOGITO_SERIALIZABLE);
+        assertThat(valueField.getOption()).isEqualTo("[kogito_java_class = 
\"java.lang.Object\"]");
+    }
+
     private void assertClassIsIncludedInSubclass(ProtoMessage superClass, 
ProtoMessage subClass) {
         for (ProtoField field : superClass.getFields()) {
             assertThat(field).isNotNull();
diff --git 
a/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/objectvariable/ObjectVariableProcess.bpmn2
 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/objectvariable/ObjectVariableProcess.bpmn2
new file mode 100644
index 00000000000..fd2eb59bc22
--- /dev/null
+++ 
b/kogito-codegen-modules/kogito-codegen-processes/src/test/resources/objectvariable/ObjectVariableProcess.bpmn2
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"; 
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"; 
xmlns:bpsim="http://www.bpsim.org/schemas/1.0"; 
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"; 
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"; 
xmlns:drools="http://www.jboss.org/drools"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
id="_B8_akJtRED-1FZqqQztbrw" 
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd 
http://www. [...]
+  <bpmn2:itemDefinition id="_objItem" structureRef="Object" />
+  <bpmn2:itemDefinition id="_E7762C91-1119-41A3-BB57-B4617F5D092B" 
structureRef="String" />
+  <bpmn2:itemDefinition id="_8EEFAA15-E15B-438F-AB1B-1BE5E57DE91C" 
structureRef="Integer" />
+  <bpmn2:collaboration id="_9211A22D-543A-46CC-A4D7-76DF2EEB4154" 
name="Default Collaboration">
+    <bpmn2:participant id="_1B027897-2CBF-4063-A335-E4F1D9E0272F" name="Pool 
Participant" processRef="objectprocess" />
+  </bpmn2:collaboration>
+  <bpmn2:process id="objectprocess" drools:packageName="com.example" 
drools:version="1.0" drools:adHoc="false" name="objectprocess" 
isExecutable="true" processType="Public">
+    <bpmn2:property id="obj" itemSubjectRef="_objItem" name="obj" />
+    <bpmn2:property id="_96C4A35F-EC53-4CCD-A8EE-0B6874D92226" name="name" 
itemSubjectRef="_E7762C91-1119-41A3-BB57-B4617F5D092B">
+      <bpmn2:extensionElements />
+    </bpmn2:property>
+    <bpmn2:property id="_AAA41D2D-8E2E-4B52-9D12-E231392389C2" name="age" 
itemSubjectRef="_8EEFAA15-E15B-438F-AB1B-1BE5E57DE91C">
+      <bpmn2:extensionElements />
+    </bpmn2:property>
+    <bpmn2:property id="_6F9BAD03-F58A-4C93-B444-A3CED8A62FF3" 
name="_6F9BAD03-F58A-4C93-B444-A3CED8A62FF3" />
+    <bpmn2:laneSet id="_51D81EEB-490C-4BD3-BD93-9F34354E7541" />
+    <bpmn2:startEvent id="_EC77F346-CFAA-4180-8459-396589D8020A">
+      <bpmn2:outgoing>_41686461-A732-4D76-BE81-4AE9E74A58F0</bpmn2:outgoing>
+    </bpmn2:startEvent>
+    <bpmn2:endEvent id="_CB939C91-EB0A-48FC-8FF6-B1E6534B969B">
+      <bpmn2:incoming>_F6A671ED-93F2-4CD3-93D9-8CA03BD4D3B5</bpmn2:incoming>
+    </bpmn2:endEvent>
+    <bpmn2:scriptTask id="_17F91537-B6F4-4232-AE8A-1FA6F7B8865D" name="Log" 
scriptFormat="http://www.java.com/java";>
+      <bpmn2:extensionElements>
+        <drools:metaData name="elementname">
+          <drools:metaValue>log</drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:incoming>_D9DA7439-23C8-4956-BDC7-EB219622F408</bpmn2:incoming>
+      <bpmn2:outgoing>_F6A671ED-93F2-4CD3-93D9-8CA03BD4D3B5</bpmn2:outgoing>
+      <bpmn2:script>System.out.println(&quot;Process started with object 
variable &quot; + obj);</bpmn2:script>
+    </bpmn2:scriptTask>
+    <bpmn2:scriptTask id="_D19B34E2-7536-4075-A0FD-0AF3E78F77F3" 
scriptFormat="http://www.java.com/java"; name="Create Person.">
+      <bpmn2:extensionElements>
+        <drools:metaData name="customAsync">
+          <drools:metaValue>false</drools:metaValue>
+        </drools:metaData>
+        <drools:metaData name="customAutoStart">
+          <drools:metaValue>false</drools:metaValue>
+        </drools:metaData>
+      </bpmn2:extensionElements>
+      <bpmn2:incoming>_41686461-A732-4D76-BE81-4AE9E74A58F0</bpmn2:incoming>
+      <bpmn2:outgoing>_D9DA7439-23C8-4956-BDC7-EB219622F408</bpmn2:outgoing>
+      <bpmn2:script>System.out.println(&quot;creating person with values: 
&quot; + name + &quot;:&quot; + age);
+
+obj = new org.kie.kogito.codegen.data.Person(name, age);
+
+kcontext.setVariable(&quot;obj&quot;, obj);</bpmn2:script>
+    </bpmn2:scriptTask>
+    <bpmn2:sequenceFlow id="_41686461-A732-4D76-BE81-4AE9E74A58F0" 
sourceRef="_EC77F346-CFAA-4180-8459-396589D8020A" 
targetRef="_D19B34E2-7536-4075-A0FD-0AF3E78F77F3" />
+    <bpmn2:sequenceFlow id="_D9DA7439-23C8-4956-BDC7-EB219622F408" 
sourceRef="_D19B34E2-7536-4075-A0FD-0AF3E78F77F3" 
targetRef="_17F91537-B6F4-4232-AE8A-1FA6F7B8865D" />
+    <bpmn2:sequenceFlow id="_F6A671ED-93F2-4CD3-93D9-8CA03BD4D3B5" 
sourceRef="_17F91537-B6F4-4232-AE8A-1FA6F7B8865D" 
targetRef="_CB939C91-EB0A-48FC-8FF6-B1E6534B969B" />
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram id="_E146D427-7409-455D-8E60-61AF7CFD8C80">
+    <bpmndi:BPMNPlane bpmnElement="objectprocess" 
id="_D756861C-0374-4F19-831B-6DE0C1DD61AE">
+      <bpmndi:BPMNShape id="shape__EC77F346-CFAA-4180-8459-396589D8020A" 
bpmnElement="_EC77F346-CFAA-4180-8459-396589D8020A">
+        <dc:Bounds height="56" width="56" x="20" y="180" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__17F91537-B6F4-4232-AE8A-1FA6F7B8865D" 
bpmnElement="_17F91537-B6F4-4232-AE8A-1FA6F7B8865D">
+        <dc:Bounds height="102" width="154" x="320" y="160" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="shape__CB939C91-EB0A-48FC-8FF6-B1E6534B969B" 
bpmnElement="_CB939C91-EB0A-48FC-8FF6-B1E6534B969B">
+        <dc:Bounds height="56" width="56" x="540" y="180" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="_46C3831B-B77D-454D-B63B-434025DEC207" 
bpmnElement="_D19B34E2-7536-4075-A0FD-0AF3E78F77F3">
+        <dc:Bounds x="100" y="160" width="180" height="100" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge 
id="_3F83D37B-DE4C-4A19-A667-2744A8824990-AUTO-SOURCE-AUTO-TARGET" 
bpmnElement="_41686461-A732-4D76-BE81-4AE9E74A58F0" 
sourceElement="shape__EC77F346-CFAA-4180-8459-396589D8020A" 
targetElement="_46C3831B-B77D-454D-B63B-434025DEC207">
+        <di:waypoint x="48" y="208" />
+        <di:waypoint x="190" y="210" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="_449D042B-8497-4FE8-8D9B-E7A038DB0E18" 
bpmnElement="_D9DA7439-23C8-4956-BDC7-EB219622F408" 
sourceElement="_46C3831B-B77D-454D-B63B-434025DEC207" 
targetElement="shape__17F91537-B6F4-4232-AE8A-1FA6F7B8865D">
+        <di:waypoint x="190" y="210" />
+        <di:waypoint x="320" y="211" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="_92AC4430-EFC8-4289-8D56-5B7443DFA6ED" 
bpmnElement="_F6A671ED-93F2-4CD3-93D9-8CA03BD4D3B5" 
sourceElement="shape__17F91537-B6F4-4232-AE8A-1FA6F7B8865D" 
targetElement="shape__CB939C91-EB0A-48FC-8FF6-B1E6534B969B">
+        <di:waypoint x="397" y="211" />
+        <di:waypoint x="540" y="208" />
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+  <bpmn2:relationship type="BPSimData">
+    <bpmn2:extensionElements>
+      <bpsim:BPSimData>
+        <bpsim:Scenario id="default" name="Simulationscenario">
+          <bpsim:ScenarioParameters />
+          <bpsim:ElementParameters 
elementRef="_EC77F346-CFAA-4180-8459-396589D8020A">
+            <bpsim:TimeParameters>
+              <bpsim:ProcessingTime>
+                <bpsim:NormalDistribution mean="0" standardDeviation="0" />
+              </bpsim:ProcessingTime>
+            </bpsim:TimeParameters>
+          </bpsim:ElementParameters>
+          <bpsim:ElementParameters 
elementRef="_17F91537-B6F4-4232-AE8A-1FA6F7B8865D">
+            <bpsim:TimeParameters>
+              <bpsim:ProcessingTime>
+                <bpsim:NormalDistribution mean="0" standardDeviation="0" />
+              </bpsim:ProcessingTime>
+            </bpsim:TimeParameters>
+            <bpsim:ResourceParameters>
+              <bpsim:Availability>
+                <bpsim:FloatingParameter value="0" />
+              </bpsim:Availability>
+              <bpsim:Quantity>
+                <bpsim:FloatingParameter value="0" />
+              </bpsim:Quantity>
+            </bpsim:ResourceParameters>
+            <bpsim:CostParameters>
+              <bpsim:UnitCost>
+                <bpsim:FloatingParameter value="0" />
+              </bpsim:UnitCost>
+            </bpsim:CostParameters>
+          </bpsim:ElementParameters>
+        </bpsim:Scenario>
+      </bpsim:BPSimData>
+    </bpmn2:extensionElements>
+    <bpmn2:source>_B8_akJtRED-1FZqqQztbrw</bpmn2:source>
+    <bpmn2:target>_B8_akJtRED-1FZqqQztbrw</bpmn2:target>
+  </bpmn2:relationship>
+</bpmn2:definitions>
diff --git 
a/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ModelMetaData.java
 
b/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ModelMetaData.java
index cdfad7af803..1432ac379ad 100644
--- 
a/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ModelMetaData.java
+++ 
b/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/ModelMetaData.java
@@ -32,6 +32,8 @@ import org.kie.kogito.codegen.Generated;
 import org.kie.kogito.codegen.VariableInfo;
 import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess;
 import org.kie.kogito.internal.utils.KogitoTags;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.github.javaparser.ast.CompilationUnit;
@@ -67,6 +69,8 @@ import static 
org.kie.kogito.internal.utils.ConversionUtils.sanitizeClassName;
 
 public class ModelMetaData {
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ModelMetaData.class);
+
     private final String processId;
     private final String packageName;
     private final String modelClassSimpleName;
@@ -223,6 +227,12 @@ public class ModelMetaData {
             String vtype = variable.getValue().getType().getStringType();
             String sanitizedName = variable.getValue().getSanitizedName();
 
+            if (Object.class.getCanonicalName().equals(vtype)) {
+                LOGGER.warn("Process variable '{}' is declared as 
java.lang.Object. " +
+                        "Object type variables are persisted using Java 
serialization. " +
+                        "Make sure the stored value implements 
java.io.Serializable.", varName);
+            }
+
             FieldDeclaration fd = declareField(sanitizedName, vtype);
             modelClass.addMember(fd);
 
diff --git 
a/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java
 
b/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java
index d422bcf4174..c37d1e869b9 100644
--- 
a/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java
+++ 
b/kogito-jbpm/jbpm-flow-builder/src/main/java/org/jbpm/compiler/canonical/WorkItemModelMetaData.java
@@ -34,6 +34,8 @@ import 
org.kie.api.definition.process.WorkflowElementIdentifier;
 import org.kie.kogito.UserTask;
 import org.kie.kogito.UserTaskParam;
 import org.kie.kogito.UserTaskParam.ParamType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.github.javaparser.ast.CompilationUnit;
 import com.github.javaparser.ast.Modifier;
@@ -72,6 +74,8 @@ import static 
org.kie.kogito.internal.utils.ConversionUtils.sanitizeJavaName;
 
 public class WorkItemModelMetaData {
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(WorkItemModelMetaData.class);
+
     private static final String TASK_INTPUT_CLASS_SUFFIX = "TaskInput";
     private static final String TASK_OUTTPUT_CLASS_SUFFIX = "TaskOutput";
     private static final String TASK_MODEL_CLASS_SUFFIX = "TaskModel";
@@ -206,10 +210,17 @@ public class WorkItemModelMetaData {
                 }
             }
 
+            String variableType = variable.getType().getStringType();
+            if (Object.class.getCanonicalName().equals(variableType)) {
+                LOGGER.warn("User task input variable '{}' is declared as 
java.lang.Object. " +
+                        "Object type variables are persisted using Java 
serialization. " +
+                        "Make sure the stored value implements 
java.io.Serializable.", entry.getKey());
+            }
+
             String fieldName = sanitizeJavaName(entry.getKey());
             FieldDeclaration fd = new FieldDeclaration().addVariable(
                     new VariableDeclarator()
-                            .setType(variable.getType().getStringType())
+                            .setType(variableType)
                             .setName(fieldName))
                     .addModifier(Modifier.Keyword.PRIVATE);
 
@@ -223,7 +234,7 @@ public class WorkItemModelMetaData {
             // from static method body — sanitised name for the identifier, 
original key for the runtime map lookup
             FieldAccessExpr field = new FieldAccessExpr(item, fieldName);
 
-            ClassOrInterfaceType type = 
parseClassOrInterfaceType(variable.getType().getStringType());
+            ClassOrInterfaceType type = 
parseClassOrInterfaceType(variableType);
             staticFromMap.addStatement(new AssignExpr(field, new CastExpr(
                     type,
                     new MethodCallExpr(
@@ -320,10 +331,17 @@ public class WorkItemModelMetaData {
                 }
             }
 
+            String variableType = variable.getType().getStringType();
+            if (Object.class.getCanonicalName().equals(variableType)) {
+                LOGGER.warn("User task output variable '{}' is declared as 
java.lang.Object. " +
+                        "Object type variables are persisted using Java 
serialization. " +
+                        "Make sure the stored value implements 
java.io.Serializable.", entry.getKey());
+            }
+
             String fieldName = sanitizeJavaName(entry.getKey());
             FieldDeclaration fd = new FieldDeclaration().addVariable(
                     new VariableDeclarator()
-                            .setType(variable.getType().getStringType())
+                            .setType(variableType)
                             .setName(fieldName))
                     .addModifier(Modifier.Keyword.PRIVATE);
             modelClass.addMember(fd);
@@ -340,7 +358,7 @@ public class WorkItemModelMetaData {
 
             // fromMap method body — original key for params.get(), sanitised 
name for field assignment
             fromMapBody.addStatement(new AssignExpr(new 
FieldAccessExpr(fromMapReturn, fieldName),
-                    new CastExpr(new ClassOrInterfaceType(null, 
variable.getType().getStringType()),
+                    new CastExpr(new ClassOrInterfaceType(null, variableType),
                             new MethodCallExpr(params, "get").addArgument(new 
StringLiteralExpr(entry.getKey()))),
                     Operator.ASSIGN));
 
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
new file mode 100644
index 00000000000..fddfcc20b8f
--- /dev/null
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/JavaSerializationUtils.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jbpm.flow.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+
+public final class JavaSerializationUtils {
+
+    private JavaSerializationUtils() {
+    }
+
+    /**
+     * Serializes {@code value} to a byte array using Java serialization.
+     * <p>
+     * ObjectOutputStream must be closed before calling toByteArray(): close() 
flushes the final
+     * block header; without it the byte array is truncated and causes 
EOFException on deserialization.
+     */
+    public static byte[] serialize(Object value) throws IOException {
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
+                out.writeObject(value);
+            }
+            return stream.toByteArray();
+        }
+    }
+
+    /**
+     * Deserializes {@code data} using Java serialization, resolving classes 
through the thread
+     * context classloader first so that application classes are found in 
environments like Quarkus
+     * where the bootstrap classloader does not see deployment classes.
+     */
+    public static Object deserialize(byte[] data) throws IOException, 
ClassNotFoundException {
+        try (InputStream is = new ByteArrayInputStream(data); 
ObjectInputStream ois = new ObjectInputStream(is) {
+            @Override
+            protected Class<?> resolveClass(ObjectStreamClass desc) throws 
IOException, ClassNotFoundException {
+                try {
+                    return Class.forName(desc.getName(), false, 
Thread.currentThread().getContextClassLoader());
+                } catch (ClassNotFoundException ex) {
+                    return super.resolveClass(desc);
+                }
+            }
+        }) {
+            return ois.readObject();
+        }
+    }
+}
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategy.java
 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategy.java
index 791625afc0f..42a63b4e64d 100644
--- 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategy.java
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/impl/marshallers/ProtobufObjectMarshallerStrategy.java
@@ -18,21 +18,15 @@
  */
 package org.jbpm.flow.serialization.impl.marshallers;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.ObjectStreamClass;
 
+import org.jbpm.flow.serialization.JavaSerializationUtils;
 import org.jbpm.flow.serialization.ObjectMarshallerStrategy;
 import org.jbpm.flow.serialization.ProcessInstanceMarshallerException;
 
 import com.google.protobuf.Any;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.BytesValue;
-import com.google.protobuf.InvalidProtocolBufferException;
 
 public class ProtobufObjectMarshallerStrategy implements 
ObjectMarshallerStrategy {
 
@@ -53,9 +47,9 @@ public class ProtobufObjectMarshallerStrategy implements 
ObjectMarshallerStrateg
 
     @Override
     public Any marshall(Object unmarshalled) {
-        try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
ObjectOutputStream out = new ObjectOutputStream(stream)) {
-            out.writeObject(unmarshalled);
-            return 
Any.pack(BytesValue.of(ByteString.copyFrom(stream.toByteArray())));
+        try {
+            byte[] bytes = JavaSerializationUtils.serialize(unmarshalled);
+            return Any.pack(BytesValue.of(ByteString.copyFrom(bytes)));
         } catch (IOException e) {
             throw new ProcessInstanceMarshallerException("Not possible to 
unmarshall value: " + unmarshalled, e);
         }
@@ -68,26 +62,9 @@ public class ProtobufObjectMarshallerStrategy implements 
ObjectMarshallerStrateg
             if (ByteString.EMPTY.equals(storedValue.getValue())) {
                 return null;
             }
-            return readObject(storedValue.getValue().toByteArray());
-        } catch (InvalidProtocolBufferException e1) {
-            throw new ProcessInstanceMarshallerException("Unexpected error 
during protobuf object unmarshalling", e1);
-        }
-    }
-
-    private Object readObject(byte[] data) {
-        try (InputStream is = new ByteArrayInputStream(data); 
ObjectInputStream ois = new ObjectInputStream(is) {
-            @Override
-            protected Class<?> resolveClass(ObjectStreamClass desc) throws 
IOException, ClassNotFoundException {
-                try {
-                    return Class.forName(desc.getName(), false, 
Thread.currentThread().getContextClassLoader());
-                } catch (ClassNotFoundException ex) {
-                    return super.resolveClass(desc);
-                }
-            }
-        }) {
-            return ois.readObject();
+            return 
JavaSerializationUtils.deserialize(storedValue.getValue().toByteArray());
         } catch (IOException | ClassNotFoundException e) {
-            throw new ProcessInstanceMarshallerException("Unexpected error 
while trying to read object", e);
+            throw new ProcessInstanceMarshallerException("Unexpected error 
during protobuf object unmarshalling", e);
         }
     }
 
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshaller.java
 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshaller.java
index c97b94e9448..aa8c0cbd36b 100644
--- 
a/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshaller.java
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/main/java/org/jbpm/flow/serialization/marshaller/SerializableProtostreamBaseMarshaller.java
@@ -18,15 +18,11 @@
  */
 package org.jbpm.flow.serialization.marshaller;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
 import java.io.Serializable;
 
 import org.infinispan.protostream.MessageMarshaller;
+import org.jbpm.flow.serialization.JavaSerializationUtils;
 import org.jbpm.flow.serialization.ProcessInstanceMarshallerException;
 
 public class SerializableProtostreamBaseMarshaller implements 
MessageMarshaller<Serializable> {
@@ -43,25 +39,24 @@ public class SerializableProtostreamBaseMarshaller 
implements MessageMarshaller<
 
     @Override
     public Serializable readFrom(ProtoStreamReader reader) throws IOException {
-        return (Serializable) readObject(reader.readBytes("data"));
+        byte[] data = reader.readBytes("data");
+        if (data == null || data.length == 0) {
+            return null;
+        }
+        try {
+            return (Serializable) JavaSerializationUtils.deserialize(data);
+        } catch (ClassNotFoundException e) {
+            throw new ProcessInstanceMarshallerException("Unexpected error 
while trying to unmarshall object", e);
+        }
     }
 
     @Override
     public void writeTo(ProtoStreamWriter writer, Serializable serializable) 
throws IOException {
-        try (ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
ObjectOutputStream out = new ObjectOutputStream(stream)) {
-            out.writeObject(serializable);
-            writer.writeBytes("data", stream.toByteArray());
+        try {
+            writer.writeBytes("data", 
JavaSerializationUtils.serialize(serializable));
         } catch (IOException e) {
             throw new ProcessInstanceMarshallerException("Not possible to 
marshall value: " + serializable, e);
         }
     }
 
-    private Object readObject(byte[] data) {
-        try (InputStream is = new ByteArrayInputStream(data); 
ObjectInputStream ois = new ObjectInputStream(is)) {
-            return ois.readObject();
-        } catch (IOException | ClassNotFoundException e) {
-            throw new ProcessInstanceMarshallerException("Unexpected error 
while trying to unmarshall object", e);
-        }
-    }
-
 }
diff --git 
a/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
new file mode 100644
index 00000000000..7973fcf177d
--- /dev/null
+++ 
b/kogito-jbpm/process-serialization-protobuf/src/test/java/org/jbpm/flow/serialization/JavaSerializationUtilsTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.jbpm.flow.serialization;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Objects;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class JavaSerializationUtilsTest {
+
+    // -- test data ----------------------------------------------------------
+
+    static class Person implements Serializable {
+        private static final long serialVersionUID = 1L;
+        final String name;
+        final int age;
+
+        Person(String name, int age) {
+            this.name = name;
+            this.age = age;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Person))
+                return false;
+            Person p = (Person) o;
+            return age == p.age && Objects.equals(name, p.name);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name, age);
+        }
+    }
+
+    static class NotSerializable {
+        // intentionally does not implement Serializable
+    }
+
+    // -- tests --------------------------------------------------------------
+
+    @Test
+    void roundTripString() throws Exception {
+        String original = "hello world";
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+        assertThat(bytes).isNotEmpty();
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(original);
+    }
+
+    @Test
+    void roundTripCustomObject() throws Exception {
+        Person original = new Person("Alice", 30);
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+        assertThat(bytes).isNotEmpty();
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(original);
+    }
+
+    @Test
+    void roundTripNull() throws Exception {
+        byte[] bytes = JavaSerializationUtils.serialize(null);
+        assertThat(bytes).isNotEmpty(); // OOS writes a null token, not empty 
bytes
+        assertThat(JavaSerializationUtils.deserialize(bytes)).isNull();
+    }
+
+    @Test
+    void roundTripCollection() throws Exception {
+        List<String> original = List.of("a", "b", "c");
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(original);
+    }
+
+    @Test
+    void serializeBytesAreCompleteAndDeserializable() throws Exception {
+        // Regression: toByteArray() called before OOS.close() produced 
truncated bytes
+        // causing EOFException on deserialization. Verify bytes are always 
fully flushed.
+        Person original = new Person("Bob", 42);
+        byte[] bytes = JavaSerializationUtils.serialize(original);
+        // A valid Java serialization stream starts with the magic bytes 0xACED
+        assertThat(bytes[0]).isEqualTo((byte) 0xAC);
+        assertThat(bytes[1]).isEqualTo((byte) 0xED);
+        // And can be deserialized without EOFException
+        
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(original);
+    }
+
+    @Test
+    void serializeNonSerializableThrows() {
+        assertThatThrownBy(() -> JavaSerializationUtils.serialize(new 
NotSerializable()))
+                .isInstanceOf(IOException.class);
+    }
+
+    @Test
+    void deserializeCorruptBytesThrows() {
+        byte[] garbage = new byte[] { 0x00, 0x01, 0x02, 0x03 };
+        assertThatThrownBy(() -> JavaSerializationUtils.deserialize(garbage))
+                .isInstanceOf(IOException.class);
+    }
+
+    @Test
+    void deserializeUsesThreadContextClassLoader() throws Exception {
+        // Verify that deserialize resolves classes through the thread-context 
classloader.
+        // We swap in a classloader that can see Person, confirm round-trip 
still works.
+        ClassLoader original = Thread.currentThread().getContextClassLoader();
+        try {
+            
Thread.currentThread().setContextClassLoader(Person.class.getClassLoader());
+            Person person = new Person("Carol", 25);
+            byte[] bytes = JavaSerializationUtils.serialize(person);
+            
assertThat(JavaSerializationUtils.deserialize(bytes)).isEqualTo(person);
+        } finally {
+            Thread.currentThread().setContextClassLoader(original);
+        }
+    }
+}
diff --git 
a/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/main/java/org/kie/kogito/quarkus/workflow/deployment/JandexProtoGenerator.java
 
b/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/main/java/org/kie/kogito/quarkus/workflow/deployment/JandexProtoGenerator.java
index 9aabb4dc569..46ce5f2b616 100644
--- 
a/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/main/java/org/kie/kogito/quarkus/workflow/deployment/JandexProtoGenerator.java
+++ 
b/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/main/java/org/kie/kogito/quarkus/workflow/deployment/JandexProtoGenerator.java
@@ -137,6 +137,7 @@ public class JandexProtoGenerator extends 
AbstractProtoGenerator<ClassInfo> {
             String fieldTypeString = pd.type().name().toString();
 
             DotName fieldType = pd.type().name();
+            LOGGER.debug("Resolving proto type for field '{}' of type '{}' in 
'{}'", pd.name(), fieldTypeString, clazz.name());
             String protoType;
             if (isArray(pd)) {
                 fieldTypeString = ARRAY;
diff --git 
a/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/test/java/org/kie/kogito/quarkus/workflow/deployment/JandexTestUtils.java
 
b/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/test/java/org/kie/kogito/quarkus/workflow/deployment/JandexTestUtils.java
index 2201a3d51be..82611c52138 100644
--- 
a/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/test/java/org/kie/kogito/quarkus/workflow/deployment/JandexTestUtils.java
+++ 
b/kogito-quarkus/extensions/kogito-quarkus-workflow-extension-common/kogito-quarkus-workflow-common-deployment/src/test/java/org/kie/kogito/quarkus/workflow/deployment/JandexTestUtils.java
@@ -41,6 +41,8 @@ import org.kie.kogito.codegen.data.HelloModel;
 import org.kie.kogito.codegen.data.JacksonData;
 import org.kie.kogito.codegen.data.ListWithoutType;
 import org.kie.kogito.codegen.data.NotEmptyConstructor;
+import org.kie.kogito.codegen.data.ObjectHolder;
+import org.kie.kogito.codegen.data.ObjectProcessModel;
 import org.kie.kogito.codegen.data.Person;
 import org.kie.kogito.codegen.data.PersonSubClass;
 import org.kie.kogito.codegen.data.PersonVarInfo;
@@ -82,6 +84,8 @@ public final class JandexTestUtils {
             PersonSubClass.class,
             JacksonData.class,
             ListWithoutType.class,
+            ObjectHolder.class,
+            ObjectProcessModel.class,
             PersonWithBooleanObject.class,
             PersonWithBooleanGetAccessor.class);
 


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

Reply via email to