You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2020/12/25 14:45:53 UTC

[ignite-3] 04/04: Fix generated serializer linkage errors.

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

amashenkov pushed a commit to branch gg-13618-asm
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 0499142e36f92fb146daff8c51b568b6716f3adb
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Fri Dec 25 15:00:28 2020 +0300

    Fix generated serializer linkage errors.
---
 .../marshaller/asm/AsmSerializerGenerator.java     | 316 +++++++++++----------
 .../asm/IdentityMarshallerCodeGenerator.java       |  17 +-
 .../marshaller/asm/MarshallerCodeGenerator.java    |  14 +-
 .../asm/ObjectMarshallerCodeGenerator.java         |  47 ++-
 .../IdentityObjectMarshallerExprGenerator.java     |   2 +-
 .../codegen/MarshallerCodeGenerator.java           |  10 +-
 .../codegen/ObjectMarshallerCodeGenerator.java     |  18 +-
 .../marshaller/codegen/SerializerGenerator.java    |  75 +++--
 .../schema/marshaller/JavaSerializerTest.java      |  93 +++++-
 9 files changed, 365 insertions(+), 227 deletions(-)

diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
index 4c151a9..d49c7a7 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/AsmSerializerGenerator.java
@@ -21,16 +21,17 @@ import com.facebook.presto.bytecode.Access;
 import com.facebook.presto.bytecode.BytecodeBlock;
 import com.facebook.presto.bytecode.ClassDefinition;
 import com.facebook.presto.bytecode.ClassGenerator;
+import com.facebook.presto.bytecode.FieldDefinition;
 import com.facebook.presto.bytecode.MethodDefinition;
 import com.facebook.presto.bytecode.Parameter;
 import com.facebook.presto.bytecode.ParameterizedType;
 import com.facebook.presto.bytecode.Scope;
 import com.facebook.presto.bytecode.Variable;
 import com.facebook.presto.bytecode.control.IfStatement;
+import com.facebook.presto.bytecode.control.TryCatch;
 import com.facebook.presto.bytecode.expression.BytecodeExpression;
 import com.facebook.presto.bytecode.expression.BytecodeExpressions;
 import java.io.StringWriter;
-import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.concurrent.TimeUnit;
@@ -49,7 +50,6 @@ import org.apache.ignite.internal.schema.marshaller.SerializationException;
 import org.apache.ignite.internal.schema.marshaller.Serializer;
 import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
 import org.apache.ignite.internal.util.ObjectFactory;
-import org.jetbrains.annotations.NotNull;
 
 /**
  * {@link Serializer} code generator.
@@ -68,6 +68,7 @@ public class AsmSerializerGenerator implements SerializerFactory {
         Class<?> keyClass,
         Class<?> valClass
     ) {
+        final boolean isDebugEnabled = true; // log.isDebugEnabled();
         final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
 
         final StringWriter writer = new StringWriter();
@@ -80,34 +81,58 @@ public class AsmSerializerGenerator implements SerializerFactory {
             long compilationTime = System.nanoTime();
             generation = compilationTime - generation;
 
-            final Class<? extends Serializer> aClass = ClassGenerator.classGenerator(getClassLoader())
-                .fakeLineNumbers(true)
-                .runAsmVerifier(true)
-                .dumpRawBytecode(true)
-                .dumpClassFilesTo(Paths.get("./target"))
-//                .outputTo(writer)
-                .defineClass(classDef, Serializer.class);
+            final ClassGenerator generator = ClassGenerator.classGenerator(getClassLoader());
+
+            if (isDebugEnabled) {
+                generator.outputTo(writer)
+                    .fakeLineNumbers(true)
+                    .runAsmVerifier(true)
+                    .dumpRawBytecode(true);
+            }
+
+            final Class<? extends Serializer> aClass = generator.defineClass(classDef, Serializer.class);
 
             compilationTime = System.nanoTime() - compilationTime;
 
-            //TODO: pass code to logger on trace level.
-            System.out.println("Serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generation) + "us\n" +
-                ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilationTime) + "us\n" +
-                writer.toString());
+            if (isDebugEnabled) //TODO: pass code to logger on debug level.
+                System.out.println("ASM serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generation) + "us" +
+                    ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilationTime) + "us." + writer.toString());
 
             // Instantiate serializer.
-            return aClass.getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
-                .newInstance(schema, keyClass, valClass);
+            return aClass
+                .getDeclaredConstructor(
+                    SchemaDescriptor.class,
+                    ObjectFactory.class,
+                    ObjectFactory.class)
+                .newInstance(
+                    schema,
+                    MarshallerUtil.factoryForClass(keyClass),
+                    MarshallerUtil.factoryForClass(valClass));
 
         }
-        catch (Exception e) {
+        catch (Exception | LinkageError e) {
             throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
                 ", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName() + " code=\n" + writer.toString(), e);
         }
     }
 
-    @NotNull private ClassDefinition generateSerializerClass(String className,
-        SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass) throws ReflectiveOperationException {
+    /**
+     * Generates serializer class definition.
+     *
+     * @param className Serializer class name.
+     * @param schema Schema descriptor.
+     * @param keyClass Key class.
+     * @param valClass Value class.
+     * @return Generated java class definition.
+     */
+    private ClassDefinition generateSerializerClass(
+        String className,
+        SchemaDescriptor schema,
+        Class<?> keyClass,
+        Class<?> valClass
+    ) {
+        MarshallerCodeGenerator keyMarsh = createMarshaller(keyClass, schema.keyColumns(), 0);
+        MarshallerCodeGenerator valMarsh = createMarshaller(valClass, schema.valueColumns(), schema.keyColumns().length());
 
         final ClassDefinition classDef = new ClassDefinition(
             EnumSet.of(Access.PUBLIC),
@@ -117,12 +142,15 @@ public class AsmSerializerGenerator implements SerializerFactory {
 
         classDef.declareAnnotation(Generated.class).setValue("value", getClass().getCanonicalName());
 
-        MarshallerCodeGenerator keyMarsh = createMarshaller(keyClass, schema.keyColumns(), 0);
-        MarshallerCodeGenerator valMarsh = createMarshaller(valClass, schema.valueColumns(), schema.keyColumns().length());
+        final FieldDefinition keyClassField = classDef.declareField(EnumSet.of(Access.PRIVATE, Access.STATIC, Access.FINAL),
+            "KEY_CLASS", Class.class);
+        final FieldDefinition valueClassField = classDef.declareField(EnumSet.of(Access.PRIVATE, Access.STATIC, Access.FINAL),
+            "VALUE_CLASS", Class.class);
 
-        generateStaticHandlers(classDef, keyMarsh, valMarsh);
+        keyMarsh.initStaticHandlers(classDef, keyClassField);
+        valMarsh.initStaticHandlers(classDef, valueClassField);
 
-        generateConstructor(classDef);
+        generateFieldsAndConstructor(classDef);
         generateAssemblerFactoryMethod(classDef, schema, keyMarsh, valMarsh);
 
         generateSerializeMethod(classDef, keyMarsh, valMarsh);
@@ -132,46 +160,66 @@ public class AsmSerializerGenerator implements SerializerFactory {
         return classDef;
     }
 
-    private void generateStaticHandlers(
-        ClassDefinition classDef,
-        MarshallerCodeGenerator keyMarsh,
-        MarshallerCodeGenerator valMarsh
+    /**
+     * Creates marshaller code generator for given class.
+     *
+     * @param tClass Target class.
+     * @param columns Columns that tClass mapped to.
+     * @param firstColIdx First column absolute index in schema.
+     * @return Marshaller code generator.
+     */
+    private static MarshallerCodeGenerator createMarshaller(
+        Class<?> tClass,
+        Columns columns,
+        int firstColIdx
     ) {
-        keyMarsh.initStaticHandlers(classDef);
-        valMarsh.initStaticHandlers(classDef);
-    }
-
-    @NotNull
-    private static MarshallerCodeGenerator createMarshaller(Class<?> tClass, Columns columns, int firstColumnIdx) {
         final BinaryMode mode = MarshallerUtil.mode(tClass);
 
         if (mode == null)
-            return new ObjectMarshallerCodeGenerator(columns, tClass, firstColumnIdx);
+            return new ObjectMarshallerCodeGenerator(columns, tClass, firstColIdx);
         else
-            return new IdentityMarshallerCodeGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColumnIdx));
+            return new IdentityMarshallerCodeGenerator(tClass, TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
     }
 
-    private void generateConstructor(ClassDefinition classDef) {
+    /**
+     * Generates fields and constructor.
+     *
+     * @param classDef Serializer class definition.
+     */
+    private void generateFieldsAndConstructor(ClassDefinition classDef) {
+        classDef.declareField(EnumSet.of(Access.PRIVATE, Access.FINAL), "keyFactory", ParameterizedType.type(ObjectFactory.class));
+        classDef.declareField(EnumSet.of(Access.PRIVATE, Access.FINAL), "valFactory", ParameterizedType.type(ObjectFactory.class));
+
         final MethodDefinition constrDef = classDef.declareConstructor(
             EnumSet.of(Access.PUBLIC),
             Parameter.arg("schema", SchemaDescriptor.class),
-            Parameter.arg("keyClass", Class.class),
-            Parameter.arg("valClass", Class.class)
+            Parameter.arg("keyFactory", ParameterizedType.type(ObjectFactory.class)),
+            Parameter.arg("valFactory", ParameterizedType.type(ObjectFactory.class))
         );
 
         constrDef.getBody()
             .append(constrDef.getThis())
             .append(constrDef.getScope().getVariable("schema"))
             .invokeConstructor(classDef.getSuperClass(), ParameterizedType.type(SchemaDescriptor.class))
+            .append(constrDef.getThis().setField("keyFactory", constrDef.getScope().getVariable("keyFactory")))
+            .append(constrDef.getThis().setField("valFactory", constrDef.getScope().getVariable("valFactory")))
             .ret();
     }
 
+    /**
+     * Generates helper method.
+     *
+     * @param classDef Serializer class definition.
+     * @param schema Schema descriptor.
+     * @param keyMarsh Key marshaller code generator.
+     * @param valMarsh Value marshaller code generator.
+     */
     private void generateAssemblerFactoryMethod(
         ClassDefinition classDef,
         SchemaDescriptor schema,
         MarshallerCodeGenerator keyMarsh,
         MarshallerCodeGenerator valMarsh
-    ) throws ReflectiveOperationException {
+    ) {
         final MethodDefinition methodDef = classDef.declareMethod(
             EnumSet.of(Access.PROTECTED),
             "createAssembler",
@@ -250,21 +298,18 @@ public class AsmSerializerGenerator implements SerializerFactory {
         body.retObject();
     }
 
-    @NotNull private BytecodeExpression getColumnValueSize(Variable obj, Variable cols, int i) {
-        return BytecodeExpressions.invokeStatic(MarshallerUtil.class, "getValueSize",
-            int.class,
-            Arrays.asList(Object.class, NativeType.class),
-            obj,
-            cols.invoke("column", Column.class, BytecodeExpressions.constantInt(i))
-                .invoke("type", NativeType.class)
-        );
-    }
-
+    /**
+     * Generates serialize method.
+     *
+     * @param classDef Serializer class definition.
+     * @param keyMarsh Key marshaller code generator.
+     * @param valMarsh Value marshaller code generator.
+     */
     private void generateSerializeMethod(
         ClassDefinition classDef,
         MarshallerCodeGenerator keyMarsh,
         MarshallerCodeGenerator valMarsh
-    ) throws ReflectiveOperationException {
+    ) {
         final MethodDefinition methodDef = classDef.declareMethod(
             EnumSet.of(Access.PROTECTED),
             "serialize0",
@@ -272,26 +317,25 @@ public class AsmSerializerGenerator implements SerializerFactory {
             Parameter.arg("asm", TupleAssembler.class),
             Parameter.arg("key", Object.class),
             Parameter.arg("val", Object.class)
-        )
-            .addException(SerializationException.class);
+        ).addException(SerializationException.class);
+
         methodDef.declareAnnotation(Override.class);
 
         final Variable asm = methodDef.getScope().getVariable("asm");
 
-        methodDef.getBody()
-            .append(new IfStatement().condition(BytecodeExpressions.isNull(asm)).ifTrue(
-                new BytecodeBlock()
-                    .append(BytecodeExpressions.newInstance(IllegalStateException.class, BytecodeExpressions.constantString("ASM can't be null.")))
-                    .throwObject()
-            ));
-
-        methodDef.getBody()
-            .append(
-                keyMarsh.marshallObject(
-                    classDef.getType(),
-                    asm,
-                    methodDef.getScope().getVariable("key"))
-            )
+        methodDef.getBody().append(new IfStatement().condition(BytecodeExpressions.isNull(asm)).ifTrue(
+            new BytecodeBlock()
+                .append(BytecodeExpressions.newInstance(IllegalStateException.class, BytecodeExpressions.constantString("ASM can't be null.")))
+                .throwObject()
+        ));
+
+        final BytecodeBlock block = new BytecodeBlock();
+        block.append(
+            keyMarsh.marshallObject(
+                classDef.getType(),
+                asm,
+                methodDef.getScope().getVariable("key"))
+        )
             .append(
                 valMarsh.marshallObject(
                     classDef.getType(),
@@ -300,120 +344,100 @@ public class AsmSerializerGenerator implements SerializerFactory {
             )
             .append(asm.invoke("build", byte[].class))
             .retObject();
+
+        final Variable ex = methodDef.getScope().createTempVariable(Throwable.class);
+        methodDef.getBody().append(new TryCatch(
+            block,
+            new BytecodeBlock()
+                .putVariable(ex)
+                .append(BytecodeExpressions.newInstance(SerializationException.class, ex))
+                .throwObject(),
+            ParameterizedType.type(Throwable.class)
+        ));
+
     }
 
-    private void generateDeserializeKeyMethod(ClassDefinition classDef,
-        MarshallerCodeGenerator keyMarsh) throws ReflectiveOperationException {
+    /**
+     * Generates deserialize method.
+     *
+     * @param classDef Serializer class definition.
+     * @param keyMarsh Key marshaller code generator.
+     */
+    private void generateDeserializeKeyMethod(ClassDefinition classDef, MarshallerCodeGenerator keyMarsh) {
         final MethodDefinition methodDef = classDef.declareMethod(
             EnumSet.of(Access.PROTECTED),
             "deserializeKey0",
             ParameterizedType.type(Object.class),
             Parameter.arg("tuple", Tuple.class)
-        )
-            .addException(SerializationException.class);
+        ).addException(SerializationException.class);
+
         methodDef.declareAnnotation(Override.class);
 
         final Variable obj = methodDef.getScope().declareVariable(Object.class, "obj");
 
+        if (!keyMarsh.isSimpleType())
+            methodDef.getBody().append(obj.set(methodDef.getThis().getField("keyFactory", ObjectFactory.class)
+                .invoke("create", Object.class)));
+
         methodDef.getBody()
             .append(keyMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
             .append(obj)
             .retObject();
     }
 
-    private void generateDeserializeValueMethod(ClassDefinition classDef,
-        MarshallerCodeGenerator valMarsh) throws ReflectiveOperationException {
+    /**
+     * Generates serialize method.
+     *
+     * @param classDef Serializer class definition.
+     * @param valMarsh Value marshaller code generator.
+     */
+    private void generateDeserializeValueMethod(ClassDefinition classDef, MarshallerCodeGenerator valMarsh) {
         final MethodDefinition methodDef = classDef.declareMethod(
             EnumSet.of(Access.PROTECTED),
             "deserializeValue0",
             ParameterizedType.type(Object.class),
             Parameter.arg("tuple", Tuple.class)
-        )
-            .addException(SerializationException.class);
+        ).addException(SerializationException.class);
+
         methodDef.declareAnnotation(Override.class);
 
         final Variable obj = methodDef.getScope().declareVariable(Object.class, "obj");
+        final BytecodeBlock block = new BytecodeBlock();
 
-        methodDef.getBody()
-            .append(valMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
+        if (!valMarsh.isSimpleType())
+            block.append(obj.set(methodDef.getThis().getField("valFactory", ObjectFactory.class)
+                .invoke("create", Object.class)));
+
+        block.append(valMarsh.unmarshallObject(classDef.getType(), methodDef.getScope().getVariable("tuple"), obj))
             .append(obj)
             .retObject();
+
+        methodDef.getBody().append(block);
     }
 
-//    /**
-//     * @param valMarsh Value marshaller code generator.
-//     * @return Deserialize value method spec.
-//     */
-//    private MethodSpec generateDeserializeValueMethod(MarshallerCodeGenerator valMarsh) {
-//        return MethodSpec
-//            .methodBuilder("deserializeValue0")
-//            .addAnnotation(Override.class)
-//            .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
-//            .addParameter(Tuple.class, "tuple", Modifier.FINAL)
-//            .addException(SerializationException.class)
-//            .returns(TypeName.OBJECT)
-//
-//            .beginControlFlow("try")
-//            .addCode(valMarsh.unmarshallObjectCode("tuple"))
-//            .nextControlFlow("catch($T th)", Throwable.class)
-//            .addStatement("throw new $T(th)", SerializationException.class)
-//            .endControlFlow()
-//            .build();
-//    }
-//
-//    /**
-//     * @param keyMarsh Key marshaller code generator.
-//     * @return Deserialize key method spec.
-//     */
-//    private MethodSpec generateDeserializeKeyMethod(MarshallerCodeGenerator keyMarsh) {
-//        return MethodSpec
-//            .methodBuilder("deserializeKey0")
-//            .addAnnotation(Override.class)
-//            .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
-//            .addParameter(Tuple.class, "tuple", Modifier.FINAL)
-//            .addException(SerializationException.class)
-//            .returns(TypeName.OBJECT)
-//
-//            .beginControlFlow("try")
-//            .addCode(keyMarsh.unmarshallObjectCode("tuple"))
-//            .nextControlFlow("catch($T th)", Throwable.class)
-//            .addStatement("throw new $T(th)", SerializationException.class)
-//            .endControlFlow()
-//            .build();
-//    }
-//
-//    /**
-//     * Creates marshaller code generator for given class.
-//     *
-//     * @param tClass Target class.
-//     * @param factoryRefVar Object factory variable.
-//     * @param columns Columns that tClass mapped to.
-//     * @param firstColIdx First column absolute index in schema.
-//     * @return Marshaller code generator.
-//     */
-//    private MarshallerCodeGenerator createObjectMarshaller(
-//        Class<?> tClass,
-//        @Nullable String factoryRefVar,
-//        Columns columns,
-//        int firstColIdx
-//    ) {
-//        BinaryMode mode = MarshallerUtil.mode(tClass);
-//
-//        if (mode != null) // Simple type.
-//            return new IdentityObjectMarshallerExprGenerator(TupleColumnAccessCodeGenerator.createAccessor(mode, firstColIdx));
-//        else
-//            return new ObjectMarshallerCodeGenerator(tClass, factoryRefVar, columns, firstColIdx);
-//    }
-
-    private static BytecodeExpression cretaObjectFactoryExpr(MethodDefinition constrDef, String aClass) {
-        return BytecodeExpressions.invokeStatic(
-            MarshallerUtil.class,
-            "factoryForClass",
-            ObjectFactory.class,
-            constrDef.getScope().getVariable(aClass)
+    /**
+     * Generates column size expression.
+     *
+     * @param obj Target object.
+     * @param cols columns.
+     * @param colIdx Column index.
+     * @return Expression.
+     */
+    private BytecodeExpression getColumnValueSize(Variable obj, Variable cols, int colIdx) {
+        return BytecodeExpressions.invokeStatic(MarshallerUtil.class, "getValueSize",
+            int.class,
+            Arrays.asList(Object.class, NativeType.class),
+            obj,
+            cols.invoke("column", Column.class, BytecodeExpressions.constantInt(colIdx))
+                .invoke("type", NativeType.class)
         );
     }
 
+    /**
+     * Resolves current classoader.
+     *
+     * @return Classloader.
+     */
     private static ClassLoader getClassLoader() {
         return Thread.currentThread().getContextClassLoader() == null ?
             ClassLoader.getSystemClassLoader() :
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
index ddf0d24..033f06c 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/IdentityMarshallerCodeGenerator.java
@@ -30,16 +30,31 @@ class IdentityMarshallerCodeGenerator implements MarshallerCodeGenerator {
     /** Object field access expression generator. */
     private final TupleColumnAccessCodeGenerator columnAccessor;
 
+    /** Target class. */
+    private final Class<?> tClass;
+
     /**
      * Constructor.
      *
+     * @param tClass Target class.
      * @param columnAccessor Tuple column code generator.
      */
-    public IdentityMarshallerCodeGenerator(TupleColumnAccessCodeGenerator columnAccessor) {
+    public IdentityMarshallerCodeGenerator(Class<?> tClass, TupleColumnAccessCodeGenerator columnAccessor) {
+        this.tClass = tClass;
         this.columnAccessor = columnAccessor;
     }
 
     /** {@inheritDoc} */
+    @Override public boolean isSimpleType() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<?> targetClass() {
+        return tClass;
+    }
+
+    /** {@inheritDoc} */
     @Override public BytecodeNode getValue(ParameterizedType type, Variable key, int i) {
         return key;
     }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
index 6442bc4..2f14409 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/MarshallerCodeGenerator.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.schema.marshaller.asm;
 
 import com.facebook.presto.bytecode.BytecodeNode;
 import com.facebook.presto.bytecode.ClassDefinition;
+import com.facebook.presto.bytecode.FieldDefinition;
 import com.facebook.presto.bytecode.ParameterizedType;
 import com.facebook.presto.bytecode.Variable;
 
@@ -27,6 +28,16 @@ import com.facebook.presto.bytecode.Variable;
  */
 public interface MarshallerCodeGenerator {
     /**
+     * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
+     */
+    boolean isSimpleType();
+
+    /**
+     * @return Target class.
+     */
+    Class<?> targetClass();
+
+    /**
      * @param serializerClass Serializer type.
      * @param obj Target object variable.
      * @param colIdx Column index.
@@ -52,8 +63,9 @@ public interface MarshallerCodeGenerator {
 
     /**
      * @param classDef Class definition.
+     * @param tClassField Target class field definition.
      */
-    default void initStaticHandlers(ClassDefinition classDef) {
+    default void initStaticHandlers(ClassDefinition classDef, FieldDefinition tClassField) {
 
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
index cda6bb6..13b3981 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/asm/ObjectMarshallerCodeGenerator.java
@@ -31,7 +31,7 @@ import com.facebook.presto.bytecode.expression.BytecodeExpressions;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
 import java.lang.reflect.Field;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumSet;
 import org.apache.ignite.internal.schema.Columns;
 import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
@@ -50,7 +50,11 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     /** Object field access expression generators. */
     private final TupleColumnAccessCodeGenerator[] columnAccessors;
 
-    public ObjectMarshallerCodeGenerator(Columns columns, Class<?> tClass, int firstColIdx) {
+    public ObjectMarshallerCodeGenerator(
+        Columns columns,
+        Class<?> tClass,
+        int firstColIdx
+    ) {
         this.columns = columns;
         this.tClass = tClass;
         columnAccessors = new TupleColumnAccessCodeGenerator[columns.length()];
@@ -68,29 +72,38 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     }
 
     /** {@inheritDoc} */
+    @Override public boolean isSimpleType() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Class<?> targetClass() {
+        return tClass;
+    }
+
+    /** {@inheritDoc} */
     @Override public BytecodeNode getValue(ParameterizedType serializerClass, Variable obj,
         int i) {
         final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
 
         return BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
-            .invoke("get", columnAccessor.mappedType(), obj.cast(tClass));
+            .invoke("get", columnAccessor.mappedType(), obj);
     }
 
     /** {@inheritDoc} */
     @Override public BytecodeBlock marshallObject(ParameterizedType serializerClass, Variable asm, Variable obj) {
         final BytecodeBlock block = new BytecodeBlock();
-        final BytecodeExpression obj0 = obj.cast(tClass);
 
         for (int i = 0; i < columns.length(); i++) {
             final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
 
             final BytecodeExpression fld = BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
-                .invoke("get", columnAccessor.mappedType(), obj0);
+                .invoke("get", columnAccessor.mappedType(), obj);
 
             final BytecodeExpression marshallNonNulExpr = asm.invoke(
                 columnAccessor.writeMethodName(),
                 void.class,
-                Arrays.asList(columnAccessor.writeArgType()),
+                Collections.singletonList(columnAccessor.writeArgType()),
                 fld.cast(columnAccessor.writeArgType()));
 
             if (columns.column(i).nullable())
@@ -109,9 +122,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     /** {@inheritDoc} */
     @Override public BytecodeBlock unmarshallObject(ParameterizedType serializerClass, Variable tuple, Variable obj) {
         final BytecodeBlock block = new BytecodeBlock();
-        final BytecodeExpression obj0 = obj.cast(tClass);
-
-        block.append(obj.set(BytecodeExpressions.newInstance(tClass)));
 
         for (int i = 0; i < columns.length(); i++) {
             final TupleColumnAccessCodeGenerator columnAccessor = columnAccessors[i];
@@ -123,7 +133,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
             );
 
             block.append(BytecodeExpressions.getStatic(serializerClass, "FIELD_HANDER_" + columnAccessor.columnIdx(), ParameterizedType.type(VarHandle.class))
-                .invoke("set", void.class, obj0, val)
+                .invoke("set", void.class, obj, val)
             );
         }
 
@@ -131,19 +141,26 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     }
 
     /** {@inheritDoc} */
-    @Override public void initStaticHandlers(ClassDefinition classDef) {
+    @Override public void initStaticHandlers(ClassDefinition classDef, FieldDefinition tClassField) {
         final MethodDefinition init = classDef.getClassInitializer();
-
-        final BytecodeBlock body = init.getBody();
         final Variable lookup = init.getScope().createTempVariable(MethodHandles.Lookup.class);
 
+        final BytecodeBlock body = init.getBody().append(
+            BytecodeExpressions.setStatic(
+                tClassField,
+                BytecodeExpressions.invokeStatic(Class.class, "forName", Class.class, BytecodeExpressions.constantString(tClass.getName()))
+            ));
+
+        if (isSimpleType())
+            return;
+
         body.append(
             lookup.set(
                 BytecodeExpressions.invokeStatic(
                     MethodHandles.class,
                     "privateLookupIn",
                     MethodHandles.Lookup.class,
-                    BytecodeExpressions.constantClass(tClass),
+                    BytecodeExpressions.getStatic(tClassField),
                     BytecodeExpressions.invokeStatic(MethodHandles.class, "lookup", MethodHandles.Lookup.class))
             ));
 
@@ -155,7 +172,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
                 BytecodeExpressions.setStatic(fld, lookup.invoke(
                     "findVarHandle",
                     VarHandle.class,
-                    BytecodeExpressions.constantClass(tClass),
+                    BytecodeExpressions.getStatic(tClassField),
                     BytecodeExpressions.constantString(columns.column(i).name()),
                     BytecodeExpressions.constantClass(columnAccessors[i].mappedType())
                 ))
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
index 77151d9..975599b 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/IdentityObjectMarshallerExprGenerator.java
@@ -60,7 +60,7 @@ class IdentityObjectMarshallerExprGenerator implements MarshallerCodeGenerator {
     }
 
     /** {@inheritDoc} */
-    @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+    @Override public void initStaticHandlers(TypeSpec.Builder builder, String tClassExpr, CodeBlock.Builder staticBuilder) {
         throw new UnsupportedOperationException("Static handlers are not applicable to simple types.");
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
index 062d773..b2547d0 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/MarshallerCodeGenerator.java
@@ -51,14 +51,8 @@ interface MarshallerCodeGenerator {
 
     /**
      * @param classBuilder Class builder.
+     * @param tClassExpr Target class expression.
      * @param staticInitBuilder Static initializer builder.
      */
-    void initStaticHandlers(TypeSpec.Builder classBuilder, CodeBlock.Builder staticInitBuilder);
-
-    /**
-     * @return Marshaller target class.
-     */
-    default Class<?> getClazz() {
-        return null;
-    }
+    void initStaticHandlers(TypeSpec.Builder classBuilder, String tClassExpr, CodeBlock.Builder staticInitBuilder);
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
index 49a123d..0330075 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/ObjectMarshallerCodeGenerator.java
@@ -34,9 +34,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     /** Target object factory var. */
     private final String objectFactoryVar;
 
-    /** Target class. */
-    private final Class<?> tClass;
-
     /** Mapped columns. */
     private final Columns columns;
 
@@ -53,7 +50,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
      */
     public ObjectMarshallerCodeGenerator(Class<?> tClass, String objectFactoryVar, Columns columns, int firstColIdx) {
         this.objectFactoryVar = objectFactoryVar;
-        this.tClass = tClass;
 
         this.columns = columns;
         columnAccessessors = new TupleColumnAccessCodeGenerator[this.columns.length()];
@@ -68,12 +64,6 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
             throw new IllegalStateException(ex);
         }
     }
-
-    /** {@inheritDoc} */
-    @Override public Class<?> getClazz() {
-        return tClass;
-    }
-
     /** {@inheritDoc} */
     @Override public boolean isSimpleType() {
         return false;
@@ -82,7 +72,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     /** {@inheritDoc} */
     @Override public CodeBlock unmarshallObjectCode(String tupleExpr) {
         final CodeBlock.Builder builder = CodeBlock.builder()
-            .addStatement("$T obj = $L.create()", tClass, objectFactoryVar);
+            .addStatement("Object obj = $L.create()", objectFactoryVar);
 
         for (int i = 0; i < columnAccessessors.length; i++)
             builder.addStatement("FIELD_HANDLE_$L.set(obj, $L)", columnAccessessors[i].columnIdx(), columnAccessessors[i].read(tupleExpr));
@@ -107,7 +97,7 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
     }
 
     /** {@inheritDoc} */
-    @Override public void initStaticHandlers(TypeSpec.Builder builder, CodeBlock.Builder staticBuilder) {
+    @Override public void initStaticHandlers(TypeSpec.Builder builder, String tClassExpr, CodeBlock.Builder staticBuilder) {
         for (int i = 0; i < columnAccessessors.length; i++) {
             builder.addField(FieldSpec.builder(
                 VarHandle.class,
@@ -117,8 +107,8 @@ class ObjectMarshallerCodeGenerator implements MarshallerCodeGenerator {
                 Modifier.STATIC)
                 .build());
 
-            staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($T.class.getDeclaredField($S))",
-                columnAccessessors[i].columnIdx(), tClass, columns.column(i).name());
+            staticBuilder.addStatement("FIELD_HANDLE_$L = lookup.unreflectVarHandle($L.getDeclaredField($S))",
+                columnAccessessors[i].columnIdx(), tClassExpr, columns.column(i).name());
         }
     }
 }
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
index 65352f1..d37bd0c 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/codegen/SerializerGenerator.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.schema.marshaller.codegen;
 import com.squareup.javapoet.AnnotationSpec;
 import com.squareup.javapoet.ArrayTypeName;
 import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.FieldSpec;
 import com.squareup.javapoet.JavaFile;
 import com.squareup.javapoet.MethodSpec;
 import com.squareup.javapoet.ParameterizedTypeName;
@@ -27,7 +28,7 @@ import com.squareup.javapoet.TypeName;
 import com.squareup.javapoet.TypeSpec;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.InvocationTargetException;
-import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.processing.Generated;
 import javax.lang.model.element.Modifier;
 import jdk.jfr.Experimental;
@@ -62,31 +63,35 @@ public class SerializerGenerator implements SerializerFactory {
         Class<?> keyClass,
         Class<?> valClass
     ) {
+        final boolean isDebugEnabled = false; // log.isDebugEnabled();
         final String className = SERIALIZER_CLASS_NAME_PREFIX + schema.version();
+
         try {
             // Generate Serializer code.
-            long generation = System.nanoTime();
+            long generated = System.nanoTime();
             JavaFile javaFile = generateSerializerClassCode(className, schema, keyClass, valClass);
-            generation = System.nanoTime() - generation;
+            generated = System.nanoTime() - generated;
 
-            //TODO: pass code to logger on trace level.
-//            System.out.println("Serializer code generated in " + TimeUnit.NANOSECONDS.toMicros(generation) + "us");
-//                        System.out.println(javaFile.toString());
+            if (isDebugEnabled)
+                System.out.println(javaFile.toString());
 
             // Compile.
             long compilation = System.nanoTime();
             ClassLoader loader = CompilerUtils.compileCode(javaFile);
             compilation = System.nanoTime() - compilation;
 
-            //            System.out.println("Serializer code compiled in " + TimeUnit.NANOSECONDS.toMicros(compilation) + "us");
+            //TODO: pass code to logger on trace level.
+            if (isDebugEnabled)
+                System.out.println("Serializer created: generated=" + TimeUnit.NANOSECONDS.toMicros(generated) + "us" +
+                    ", compiled=" + TimeUnit.NANOSECONDS.toMicros(compilation) + "us." /*+ javaFile.toString()*/);
 
             // Instantiate serializer.
             return (Serializer)loader.loadClass(javaFile.packageName + '.' + className)
-                .getDeclaredConstructor(SchemaDescriptor.class, Class.class, Class.class)
-                .newInstance(schema, keyClass, valClass);
+                .getDeclaredConstructor(SchemaDescriptor.class)
+                .newInstance(schema);
 
         }
-        catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
+        catch (InstantiationException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("Failed to create serializer for key-value pair: schemaVer=" + schema.version() +
                 ", keyClass=" + keyClass.getSimpleName() + ", valueClass=" + valClass.getSimpleName(), e);
         }
@@ -111,23 +116,21 @@ public class SerializerGenerator implements SerializerFactory {
             final TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className)
                 .superclass(AbstractSerializer.class)
                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-                .addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", getClass().getCanonicalName()).build());
+                .addAnnotation(AnnotationSpec.builder(Generated.class).addMember("value", "$S", getClass().getName()).build());
 
-            initFieldHandlers(keyMarsh, valMarsh, classBuilder);
+            initStaticFields(keyMarsh, valMarsh, keyClass, valClass, classBuilder);
 
             classBuilder
-                .addField(ParameterizedTypeName.get(ObjectFactory.class, keyClass), "keyFactory", Modifier.PRIVATE, Modifier.FINAL)
-                .addField(ParameterizedTypeName.get(ObjectFactory.class, valClass), "valFactory", Modifier.PRIVATE, Modifier.FINAL)
+                .addField(ParameterizedTypeName.get(ObjectFactory.class), "keyFactory", Modifier.PRIVATE, Modifier.FINAL)
+                .addField(ParameterizedTypeName.get(ObjectFactory.class), "valFactory", Modifier.PRIVATE, Modifier.FINAL)
                 .addMethod(
                     // Constructor.
                     MethodSpec.constructorBuilder()
                         .addModifiers(Modifier.PUBLIC)
                         .addParameter(SchemaDescriptor.class, "schema")
-                        .addParameter(Class.class, "keyClass")
-                        .addParameter(Class.class, "valClass")
                         .addStatement("super(schema)")
-                        .addStatement("this.keyFactory = $T.factoryForClass(keyClass)", MarshallerUtil.class)
-                        .addStatement("this.valFactory = $T.factoryForClass(valClass)", MarshallerUtil.class)
+                        .addStatement("this.keyFactory = $T.factoryForClass(KEY_CLASS)", MarshallerUtil.class)
+                        .addStatement("this.valFactory = $T.factoryForClass(VALUE_CLASS)", MarshallerUtil.class)
                         .build()
                 )
                 .addMethod(generateTupleAsseblerFactoryMethod(schema, keyMarsh, valMarsh))
@@ -143,7 +146,6 @@ public class SerializerGenerator implements SerializerFactory {
                 .build();
         }
         catch (Exception ex) {
-            //TODO: fallback to java serializer?
             throw new IllegalStateException(ex);
         }
     }
@@ -153,38 +155,55 @@ public class SerializerGenerator implements SerializerFactory {
      * @param valMarsh Value marshaller code generator.
      * @param classBuilder Serializer class builder.
      */
-    private void initFieldHandlers(
+    private void initStaticFields(
         MarshallerCodeGenerator keyMarsh,
         MarshallerCodeGenerator valMarsh,
+        Class<?> keyClass,
+        Class<?> valueClass,
         TypeSpec.Builder classBuilder
     ) {
-        if (keyMarsh.isSimpleType() && valMarsh.isSimpleType())
-            return; // No field hanlders needed for simple types.
+        classBuilder.addField(FieldSpec.builder(
+            Class.class,
+            "KEY_CLASS",
+            Modifier.PRIVATE,
+            Modifier.FINAL,
+            Modifier.STATIC)
+            .build());
+
+        classBuilder.addField(FieldSpec.builder(
+            Class.class,
+            "VALUE_CLASS",
+            Modifier.PRIVATE,
+            Modifier.FINAL,
+            Modifier.STATIC)
+            .build());
 
         final CodeBlock.Builder staticInitBuilder = CodeBlock.builder()
             .addStatement("$T.Lookup lookup", MethodHandles.class)
             .beginControlFlow("try");
 
+        // Avoid direct class name usage in code to avoid potential linkage errors.
+        staticInitBuilder.addStatement("KEY_CLASS = $T.forName($S)", Class.class, keyClass.getName());
+        staticInitBuilder.addStatement("VALUE_CLASS = $T.forName($S)", Class.class, valueClass.getName());
+
         if (!keyMarsh.isSimpleType()) {
             staticInitBuilder.addStatement(
-                "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+                "lookup = $T.privateLookupIn(KEY_CLASS, $T.lookup())",
                 MethodHandles.class,
-                Objects.requireNonNull(keyMarsh.getClazz()),
                 MethodHandles.class
             );
 
-            keyMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+            keyMarsh.initStaticHandlers(classBuilder, "KEY_CLASS", staticInitBuilder);
         }
 
         if (!valMarsh.isSimpleType()) {
             staticInitBuilder.addStatement(
-                "lookup = $T.privateLookupIn($T.class, $T.lookup())",
+                "lookup = $T.privateLookupIn(VALUE_CLASS, $T.lookup())",
                 MethodHandles.class,
-                Objects.requireNonNull(valMarsh.getClazz()),
                 MethodHandles.class
             );
 
-            valMarsh.initStaticHandlers(classBuilder, staticInitBuilder);
+            valMarsh.initStaticHandlers(classBuilder, "VALUE_CLASS", staticInitBuilder);
         }
 
         staticInitBuilder
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
index 4db6027..8547c8e 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
@@ -230,8 +230,8 @@ public class JavaSerializerTest {
 
         SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
 
-        final Object key = PrivateTestObject.randomObject(rnd);
-        final Object val = PrivateTestObject.randomObject(rnd);
+        final Object key = PrivateContructorTestObject.randomObject(rnd);
+        final Object val = PrivateContructorTestObject.randomObject(rnd);
 
         Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
 
@@ -262,10 +262,33 @@ public class JavaSerializerTest {
         final Object key = WrongTestObject.randomObject(rnd);
         final Object val = WrongTestObject.randomObject(rnd);
 
-        assertThrows(IllegalStateException.class,
-            () -> factory.create(schema, key.getClass(), val.getClass()),
-            "Class has no default constructor: class=org.apache.ignite.internal.schema.marshaller.JavaSerializerTest$WrongTestObject"
-        );
+        assertThrows(IllegalStateException.class, () -> factory.create(schema, key.getClass(), val.getClass()));
+    }
+
+    /**
+     *
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testPrivateClass(SerializerFactory factory) throws SerializationException {
+        Column[] cols = new Column[] {
+            new Column("pLongCol", LONG, false),
+        };
+
+        SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
+
+        final Object key = PrivateTestObject.randomObject(rnd);
+        final Object val = PrivateTestObject.randomObject(rnd);
+
+        final ObjectFactory<?> objFactory = new ObjectFactory<>(PrivateTestObject.class);
+        final Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+        byte[] bytes = serializer.serialize(key, objFactory.create());
+
+        Object key1 = serializer.deserializeKey(bytes);
+        Object val1 = serializer.deserializeValue(bytes);
+
+        assertTrue(key.getClass().isInstance(key1));
+        assertTrue(val.getClass().isInstance(val1));
     }
 
     /**
@@ -498,12 +521,12 @@ public class JavaSerializerTest {
     /**
      * Test object with private constructor.
      */
-    public static class PrivateTestObject {
+    public static class PrivateContructorTestObject {
         /**
          * @return Random TestObject.
          */
-        static PrivateTestObject randomObject(Random rnd) {
-            final PrivateTestObject obj = new PrivateTestObject();
+        static PrivateContructorTestObject randomObject(Random rnd) {
+            final PrivateContructorTestObject obj = new PrivateContructorTestObject();
 
             obj.pLongCol = rnd.nextLong();
 
@@ -516,7 +539,7 @@ public class JavaSerializerTest {
         /**
          * Private constructor.
          */
-        private PrivateTestObject() {
+        private PrivateContructorTestObject() {
         }
 
         /** {@inheritDoc} */
@@ -527,7 +550,7 @@ public class JavaSerializerTest {
             if (o == null || getClass() != o.getClass())
                 return false;
 
-            PrivateTestObject object = (PrivateTestObject)o;
+            PrivateContructorTestObject object = (PrivateContructorTestObject)o;
 
             return pLongCol == object.pLongCol;
         }
@@ -541,7 +564,7 @@ public class JavaSerializerTest {
     /**
      * Test object without default constructor.
      */
-    private static class WrongTestObject {
+    public static class WrongTestObject {
         /**
          * @return Random TestObject.
          */
@@ -555,7 +578,51 @@ public class JavaSerializerTest {
         /**
          * Private constructor.
          */
-        private WrongTestObject(long val) {
+        public WrongTestObject(long val) {
+            pLongCol = val;
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            WrongTestObject object = (WrongTestObject)o;
+
+            return pLongCol == object.pLongCol;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return Objects.hash(pLongCol);
+        }
+    }
+
+    /**
+     * Test object without default constructor.
+     */
+    private static class PrivateTestObject {
+        /**
+         * @return Random TestObject.
+         */
+        static PrivateTestObject randomObject(Random rnd) {
+            return new PrivateTestObject(rnd.nextInt());
+        }
+
+        /** Value. */
+        private long pLongCol;
+
+        /** Contructor. */
+        public PrivateTestObject() {
+        }
+
+        /**
+         * Private constructor.
+         */
+        public PrivateTestObject(long val) {
             pLongCol = val;
         }