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/10 15:01:43 UTC

[ignite-3] branch ignite-13618 created (now 2cafc7b)

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

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


      at 2cafc7b  IGNITE-13618: Provide generated and reflection-based class (de)serializers.

This branch includes the following new commits:

     new 2cafc7b  IGNITE-13618: Provide generated and reflection-based class (de)serializers.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[ignite-3] 01/01: IGNITE-13618: Provide generated and reflection-based class (de)serializers.

Posted by am...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2cafc7b399875cf0ced4e1609ed2c76f6707dad0
Author: Andrew Mashenkov <an...@gmail.com>
AuthorDate: Thu Dec 10 18:01:26 2020 +0300

    IGNITE-13618: Provide generated and reflection-based class (de)serializers.
    
    Implemented reflection-based serializer using Unsafe.
    Implemented generated serializer using Janino comp.
    Benchmark for serializer added.
---
 modules/commons/pom.xml                            |  41 ++
 .../ignite/internal/schema/TupleAssembler.java     |   4 -
 .../internal/schema/marshaller/BinaryMode.java     |  90 ++++
 .../internal/schema/marshaller/MarshallerUtil.java | 103 ++++
 .../schema/marshaller/SerializationException.java  |  43 ++
 .../internal/schema/marshaller/Serializer.java     |  42 ++
 .../schema/marshaller/SerializerFactory.java       |  52 ++
 .../generator/FieldAccessExprGenerator.java        | 360 ++++++++++++++
 .../IdentityObjectMarshallerExprGenerator.java     |  45 ++
 .../generator/JaninoSerializerGenerator.java       | 346 ++++++++++++++
 .../generator/MarshallerExprGenerator.java         |  94 ++++
 .../marshaller/reflection/JavaSerializer.java      | 316 ++++++++++++
 .../reflection/JavaSerializerFactory.java          |  32 ++
 .../schema/marshaller/reflection/Marshaller.java   | 153 ++++++
 .../marshaller/reflection/UnsafeFieldAccessor.java | 527 +++++++++++++++++++++
 .../org/apache/ignite/internal/util/Factory.java   |  32 ++
 .../ignite/internal/util/IgniteUnsafeUtils.java    | 273 +++++++++++
 .../apache/ignite/internal/util/ObjectFactory.java |  62 +++
 .../java/org/apache/ignite/internal/util/Pair.java |  57 +++
 .../benchmarks/SerializerBenchmarkTest.java        | 185 ++++++++
 .../apache/ignite/internal/schema/TestUtils.java   | 121 +++++
 .../apache/ignite/internal/schema/TupleTest.java   |  64 +--
 .../schema/marshaller/JavaSerializerTest.java      | 494 +++++++++++++++++++
 .../marshaller/reflection/FieldAccessorTest.java   | 396 ++++++++++++++++
 pom.xml                                            |   7 +-
 25 files changed, 3871 insertions(+), 68 deletions(-)

diff --git a/modules/commons/pom.xml b/modules/commons/pom.xml
index 0599be4..fc78231 100644
--- a/modules/commons/pom.xml
+++ b/modules/commons/pom.xml
@@ -42,5 +42,46 @@
             <artifactId>annotations</artifactId>
             <version>${jetbrains.annotations.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <version>${janino.version}</version>
+        </dependency>
+
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Benchmarks dependencies -->
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>${jmh.framework.verion}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>${jmh.framework.verion}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>${javax.annotation.api.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/TupleAssembler.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/TupleAssembler.java
index 45d3982..71c3eda 100644
--- a/modules/commons/src/main/java/org/apache/ignite/internal/schema/TupleAssembler.java
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/TupleAssembler.java
@@ -17,12 +17,8 @@
 
 package org.apache.ignite.internal.schema;
 
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
 import java.nio.charset.StandardCharsets;
 import java.util.BitSet;
 import java.util.UUID;
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
new file mode 100644
index 0000000..db5a77f
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/BinaryMode.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+import org.apache.ignite.internal.schema.NativeTypeSpec;
+
+/**
+ * Various read/write modes for binary objects that maps Java types to binary types.
+ */
+public enum BinaryMode {
+    /** Primitive byte. */
+    P_BYTE(NativeTypeSpec.BYTE),
+
+    /** Primitive short. */
+    P_SHORT(NativeTypeSpec.SHORT),
+
+    /** Primitive int. */
+    P_INT(NativeTypeSpec.INTEGER),
+
+    /** Primitive long. */
+    P_LONG(NativeTypeSpec.LONG),
+
+    /** Primitive float. */
+    P_FLOAT(NativeTypeSpec.FLOAT),
+
+    /** Primitive int. */
+    P_DOUBLE(NativeTypeSpec.DOUBLE),
+
+    /** */
+    BYTE(NativeTypeSpec.BYTE),
+
+    /** */
+    SHORT(NativeTypeSpec.SHORT),
+
+    /** */
+    INT(NativeTypeSpec.INTEGER),
+
+    /** */
+    LONG(NativeTypeSpec.LONG),
+
+    /** */
+    FLOAT(NativeTypeSpec.FLOAT),
+
+    /** */
+    DOUBLE(NativeTypeSpec.DOUBLE),
+
+    /** */
+    STRING(NativeTypeSpec.STRING),
+
+    /** */
+    UUID(NativeTypeSpec.UUID),
+
+    /** */
+    BYTE_ARR(NativeTypeSpec.BYTES),
+
+    /** */
+    BITSET(NativeTypeSpec.BITMASK);
+
+    /** Natove type spec. */
+    private final NativeTypeSpec typeSpec;
+
+    /**
+     * @param typeSpec Native type spec.
+     */
+    BinaryMode(NativeTypeSpec typeSpec) {
+        this.typeSpec = typeSpec;
+    }
+
+    /**
+     * @return Native type spec.
+     */
+    public NativeTypeSpec typeSpec() {
+        return typeSpec;
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
new file mode 100644
index 0000000..c111ba8
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/MarshallerUtil.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+import java.util.BitSet;
+import java.util.UUID;
+import org.apache.ignite.internal.schema.NativeType;
+import org.apache.ignite.internal.schema.TupleAssembler;
+
+/**
+ * Marshaller utility class.
+ */
+public final class MarshallerUtil {
+    /**
+     * Calculates size for serialized value of varlen type.
+     *
+     * @param val Field value.
+     * @param type Mapped type.
+     * @return Serialized value size.
+     */
+    public static int getValueSize(Object val, NativeType type) {
+        switch (type.spec()) {
+            case BYTES:
+                return ((byte[])val).length;
+
+            case STRING:
+                return TupleAssembler.utf8EncodedLength((CharSequence)val);
+
+            default:
+                throw new IllegalStateException("Unsupported test varsize type: " + type);
+        }
+    }
+
+    /**
+     * Gets binary read/write mode for given class.
+     *
+     * @param cls Type.
+     * @return Binary mode.
+     */
+    public static BinaryMode mode(Class<?> cls) {
+        assert cls != null;
+
+        // Primitives.
+        if (cls == byte.class)
+            return BinaryMode.P_BYTE;
+        else if (cls == short.class)
+            return BinaryMode.P_SHORT;
+        else if (cls == int.class)
+            return BinaryMode.P_INT;
+        else if (cls == long.class)
+            return BinaryMode.P_LONG;
+        else if (cls == float.class)
+            return BinaryMode.P_FLOAT;
+        else if (cls == double.class)
+            return BinaryMode.P_DOUBLE;
+
+            // Boxed primitives.
+        else if (cls == Byte.class)
+            return BinaryMode.BYTE;
+        else if (cls == Short.class)
+            return BinaryMode.SHORT;
+        else if (cls == Integer.class)
+            return BinaryMode.INT;
+        else if (cls == Long.class)
+            return BinaryMode.LONG;
+        else if (cls == Float.class)
+            return BinaryMode.FLOAT;
+        else if (cls == Double.class)
+            return BinaryMode.DOUBLE;
+
+            // Other types
+        else if (cls == byte[].class)
+            return BinaryMode.BYTE_ARR;
+        else if (cls == String.class)
+            return BinaryMode.STRING;
+        else if (cls == UUID.class)
+            return BinaryMode.UUID;
+        else if (cls == BitSet.class)
+            return BinaryMode.BITSET;
+
+        return null;
+    }
+
+    /**
+     * Stub.
+     */
+    private MarshallerUtil() {}
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
new file mode 100644
index 0000000..e690710
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializationException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+/**
+ * Serialization exception.
+ */
+public class SerializationException extends Exception {
+    /**
+     * Constructor.
+     *
+     * @param cause Cause.
+     */
+    public SerializationException(Throwable cause) {
+        // Used by serializers generated with Janino.
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message Message.
+     * @param cause Cause.
+     */
+    public SerializationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
new file mode 100644
index 0000000..21a1560
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/Serializer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+/**
+ * Key-value objects (de)serializer.
+ */
+public interface Serializer {
+    /**
+     * Writes key-value pair to tuple.
+     *
+     * @param key Key object.
+     * @param val Value object.
+     * @return Serialized key-value pair.
+     */
+    byte[] serialize(Object key, Object val) throws SerializationException;
+
+    /**
+     * @return Key object.
+     */
+    Object deserializeKey(byte[] data) throws SerializationException;
+
+    /**
+     * @return Value object.
+     */
+    Object deserializeValue(byte[] data) throws SerializationException;
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
new file mode 100644
index 0000000..d9a8295
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/SerializerFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator;
+import org.apache.ignite.internal.schema.marshaller.reflection.JavaSerializerFactory;
+
+/**
+ * (De)Serializer factory interface.
+ */
+@FunctionalInterface
+public interface SerializerFactory {
+    /**
+     * @return Serializer factory back by code generator.
+     */
+    public static SerializerFactory createJaninoSerializerFactory() {
+        return new JaninoSerializerGenerator();
+    }
+
+    /**
+     * @return Reflection-based serializer factory.
+     */
+    public static SerializerFactory createJavaSerializerFactory() {
+        return new JavaSerializerFactory();
+    }
+
+    /**
+     * Creates serializer.
+     *
+     * @param schema Schema descriptor.
+     * @param keyClass Key class.
+     * @param valClass Value class.
+     * @return Serializer.
+     */
+    public Serializer create(SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass);
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java
new file mode 100644
index 0000000..fb3736a
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/FieldAccessExprGenerator.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.jetbrains.annotations.Nullable;
+
+import static org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator.LF;
+
+/**
+ * Object field access expression generators.
+ */
+class FieldAccessExprGenerator {
+    /** Append null expression. */
+    private static final String WRITE_NULL_EXPR = "asm.appendNull();";
+
+    /**
+     * Created object access expressions generator.
+     *
+     * @param mode Field access binary mode.
+     * @param colIdx Column absolute index in schema.
+     * @return Object field access expressions generator.
+     */
+    static FieldAccessExprGenerator createIdentityAccessor(BinaryMode mode, int colIdx) {
+        return createAccessor(mode, colIdx, -1L);
+    }
+
+    /**
+     * Created object field access expressions generator.
+     *
+     * @param mode Field access binary mode.
+     * @param colIdx Column absolute index in schema.
+     * @param offset Object field offset.
+     * @return Object field access expressions generator.
+     */
+    static FieldAccessExprGenerator createAccessor(BinaryMode mode, int colIdx, long offset) {
+        switch (mode) {
+            case BYTE:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Byte",
+                    "tuple.byteValueBoxed",
+                    "asm.appendByte",
+                    offset);
+
+            case P_BYTE:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.byteValue",
+                    "asm.appendByte",
+                    offset,
+                    "IgniteUnsafeUtils.getByteField",
+                    "IgniteUnsafeUtils.putByteField"
+                );
+
+            case SHORT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Short",
+                    "tuple.shortValueBoxed",
+                    "asm.appendShort",
+                    offset);
+
+            case P_SHORT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.shortValue",
+                    "asm.appendShort",
+                    offset,
+                    "IgniteUnsafeUtils.getShortField",
+                    "IgniteUnsafeUtils.putShortField"
+                );
+
+            case INT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Integer",
+                    "tuple.intValueBoxed",
+                    "asm.appendInt",
+                    offset);
+
+            case P_INT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.intValue",
+                    "asm.appendInt",
+                    offset,
+                    "IgniteUnsafeUtils.getIntField",
+                    "IgniteUnsafeUtils.putIntField"
+                );
+
+            case LONG:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Long",
+                    "tuple.longValueBoxed",
+                    "asm.appendLong",
+                    offset);
+
+            case P_LONG:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.longValue",
+                    "asm.appendLong",
+                    offset,
+                    "IgniteUnsafeUtils.getLongField",
+                    "IgniteUnsafeUtils.putLongField"
+                );
+
+            case FLOAT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Float",
+                    "tuple.floatValueBoxed",
+                    "asm.appendFloat",
+                    offset);
+
+            case P_FLOAT:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.floatValue",
+                    "asm.appendFloat",
+                    offset,
+                    "IgniteUnsafeUtils.getFloatField",
+                    "IgniteUnsafeUtils.putFloatField"
+                );
+
+            case DOUBLE:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "Double",
+                    "tuple.doubleValueBoxed",
+                    "asm.appendDouble",
+                    offset);
+
+            case P_DOUBLE:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "tuple.doubleValue",
+                    "asm.appendDouble",
+                    offset,
+                    "IgniteUnsafeUtils.getDoubleField",
+                    "IgniteUnsafeUtils.putDoubleField"
+                );
+
+            case UUID:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "UUID",
+                    "tuple.uuidValue", "asm.appendUuid",
+                    offset);
+
+            case BITSET:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "BitSet",
+                    "tuple.bitmaskValue", "asm.appendBitmask",
+                    offset);
+
+            case STRING:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "String",
+                    "tuple.stringValue", "asm.appendString",
+                    offset);
+
+            case BYTE_ARR:
+                return new FieldAccessExprGenerator(
+                    colIdx,
+                    "byte[]",
+                    "tuple.bytesValue", "asm.appendBytes",
+                    offset);
+            default:
+                throw new IllegalStateException("Unsupportd binary mode");
+        }
+    }
+
+    /** Object field offset or {@code -1} for identity accessor. */
+    private final long offset;
+
+    /** Absolute schema index. */
+    private final int colIdx;
+
+    /** Class cast expression. */
+    private final String classExpr;
+
+    /** Write column value expression. */
+    private final String writeColMethod;
+
+    /** Read column value expression. */
+    private final String readColMethod;
+
+    /** Read object field expression. */
+    private final String getFieldMethod;
+
+    /** Write object field expression. */
+    private final String putFieldMethod;
+
+    /**
+     * Constructor.
+     *
+     * @param colIdx Absolute schema index in schema.
+     * @param castClassExpr Class cast expression
+     * @param readColMethod Read column value expression.
+     * @param writeColMethod Write column value expression.
+     * @param offset Field offset or {@code -1} for identity accessor.
+     */
+    private FieldAccessExprGenerator(
+        int colIdx,
+        String castClassExpr,
+        String readColMethod,
+        String writeColMethod,
+        long offset
+    ) {
+        this(colIdx, castClassExpr, readColMethod, writeColMethod, offset,
+            "IgniteUnsafeUtils.getObjectField", "IgniteUnsafeUtils.putObjectField");
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param colIdx Absolute schema index in schema.
+     * @param readColMethod Read column value expression.
+     * @param writeColMethod Write column value expression.
+     * @param offset Field offset or {@code -1} for identity accessor.
+     * @param getFieldMethod Read object field expression.
+     * @param putFieldMethod Read object field expression.
+     */
+    public FieldAccessExprGenerator(
+        int colIdx,
+        String readColMethod,
+        String writeColMethod,
+        long offset,
+        String getFieldMethod,
+        String putFieldMethod
+    ) {
+        this(colIdx, null /* primitive type */, readColMethod, writeColMethod, offset, getFieldMethod, putFieldMethod);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param colIdx Absolute schema index in schema.
+     * @param castClassExpr Class cast expression or {@code null} if not applicable.
+     * @param readColMethod Read column value expression.
+     * @param writeColMethod Write column value expression.
+     * @param offset Field offset or {@code -1} for identity accessor.
+     * @param getFieldMethod Read object field expression.
+     * @param putFieldMethod Read object field expression.
+     */
+    private FieldAccessExprGenerator(
+        int colIdx,
+        @Nullable String castClassExpr,
+        String readColMethod,
+        String writeColMethod,
+        long offset,
+        String getFieldMethod,
+        String putFieldMethod
+    ) {
+        this.offset = offset;
+        this.colIdx = colIdx;
+        this.classExpr = castClassExpr;
+        this.putFieldMethod = putFieldMethod;
+        this.getFieldMethod = getFieldMethod;
+        this.writeColMethod = writeColMethod;
+        this.readColMethod = readColMethod;
+    }
+
+    /**
+     * @return {@code true} if it is primitive typed field accessor, {@code false} otherwise.
+     */
+    private boolean isPrimitive() {
+        return classExpr == null;
+    }
+
+    /**
+     * @return {@code true} if is identity accessor, {@code false} otherwise.
+     */
+    private boolean isIdentityAccessor() {
+        return offset == -1;
+    }
+
+    /**
+     * @return Object field value access expression or object value expression for simple types.
+     */
+    public String getFieldExpr() {
+        if (isIdentityAccessor())
+            return "obj"; // Identity accessor.
+
+        return getFieldMethod + "(obj, " + offset + ')';
+    }
+
+    /**
+     * Appends write value to field expression.
+     *
+     * @param sb String bulder.
+     * @param valueExpression Value expression.
+     * @param indent Line indentation.
+     */
+    public final void appendPutFieldExpr(StringBuilder sb, String valueExpression, String indent) {
+        sb.append(indent).append(putFieldMethod).append("(obj, ").append(offset).append(", ").append(valueExpression).append(')');
+        sb.append(";" + LF);
+    }
+
+    /**
+     * Appends write value to column expression.
+     *
+     * @param sb String bulder.
+     * @param valueExpr Value expression.
+     * @param indent Line indentation.
+     */
+    public final void appendWriteColumnExpr(StringBuilder sb, String valueExpr, String indent) {
+        if (isPrimitive() || isIdentityAccessor()) {
+            // Translate to:
+            // asm.appendX((T) %value%);
+            // or for primitive value:
+            // asm.appendX(%value%);
+            sb.append(indent).append(writeColMethod).append('(');
+
+            if (classExpr != null)
+                sb.append("(").append(classExpr).append(")");
+
+            sb.append(valueExpr).append(");" + LF);
+
+            return;
+        }
+
+        assert classExpr != null;
+
+        // Translate to:
+        // { T fVal = (T)%value%;
+        //  if (fVal == null) asm.appendNull() else asm.appendX(fVal); }
+        sb.append(indent).append("{ ").append(classExpr).append(" fVal = (").append(classExpr).append(')').append(valueExpr).append(";" + LF);
+        sb.append(indent).append("if (fVal == null) " + WRITE_NULL_EXPR + LF);
+        sb.append(indent).append("else ").append(writeColMethod).append("(fVal); }" + LF);
+
+    }
+
+    /**
+     * @return Column value read expression.
+     */
+    public String readColumnExpr() {
+        return readColMethod + "(" + colIdx + ")";
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
new file mode 100644
index 0000000..e574e61
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/IdentityObjectMarshallerExprGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+
+/**
+ * Generate {@link Serializer} method's bodies for simple types.
+ */
+class IdentityObjectMarshallerExprGenerator extends MarshallerExprGenerator {
+    /**
+     * Constructor.
+     *
+     * @param accessor Object field access expression generators.
+     */
+    IdentityObjectMarshallerExprGenerator(FieldAccessExprGenerator accessor) {
+        super(null /* no instantiation needed */, new FieldAccessExprGenerator[] {accessor});
+    }
+
+    /** {@inheritDoc} */
+    @Override public void appendMarshallObjectExpr(StringBuilder sb, String indent) {
+        for (int i = 0; i < accessors.length; i++)
+            accessors[i].appendWriteColumnExpr(sb, "obj", indent);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void appendUnmarshallObjectExpr(StringBuilder sb, String indent) {
+        sb.append(indent).append("Object obj = ").append(accessors[0].readColumnExpr()).append(";" + JaninoSerializerGenerator.LF);
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java
new file mode 100644
index 0000000..654bf97
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/JaninoSerializerGenerator.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
+import org.apache.ignite.internal.util.IgniteUnsafeUtils;
+import org.codehaus.commons.compiler.CompilerFactoryFactory;
+import org.codehaus.commons.compiler.IClassBodyEvaluator;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * {@link Serializer} code generator backed with Janino.
+ */
+public class JaninoSerializerGenerator implements SerializerFactory {
+    /** Tabulate. */
+    static final String TAB = "    ";
+
+    /** Line feed. */
+    static final char LF = '\n';
+
+    /** String buffer initial size. */
+    public static final int INITIAL_BUFFER_SIZE = 8 * 1024;
+
+    /** Debug flag. */
+    private static final boolean enabledDebug = true;
+
+    /** {@inheritDoc} */
+    @Override public Serializer create(
+        SchemaDescriptor schema,
+        Class<?> keyClass,
+        Class<?> valClass
+    ) {
+        try {
+            final IClassBodyEvaluator ce = CompilerFactoryFactory.getDefaultCompilerFactory().newClassBodyEvaluator();
+
+            // Generate Serializer code.
+            String code = generateSerializerClassCode(ce, schema, keyClass, valClass);
+
+            //TODO: pass code to logger on trace level.
+
+            if (enabledDebug) {
+                ce.setDebuggingInformation(true, true, true);
+
+                //TODO: dump code to log.
+//                System.out.println(code);
+            }
+
+            try {  // Compile and load class.
+                ce.setParentClassLoader(getClass().getClassLoader());
+                ce.cook(code);
+
+                // Create and return Serializer instance.
+                final Constructor<Serializer> ctor = (Constructor<Serializer>)ce.getClazz()
+                    .getDeclaredConstructor(schema.getClass(), Class.class, Class.class);
+
+                return ctor.newInstance(schema, keyClass, valClass);
+            }
+            catch (Exception ex) {
+                if (enabledDebug)
+                    throw new IllegalStateException("Failed to compile/instantiate generated Serializer: code=" +
+                        LF + code + LF, ex);
+                else
+                    throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);
+            }
+        }
+        catch (Exception ex) {
+            //TODO: fallback to java serializer?
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    /**
+     * Generates serializer code.
+     *
+     * @param ce Class body evaluator.
+     * @param schema Schema descriptor.
+     * @param keyClass Key class.
+     * @param valClass Value class.
+     * @return Generated class code.
+     */
+    private String generateSerializerClassCode(
+        IClassBodyEvaluator ce,
+        SchemaDescriptor schema,
+        Class<?> keyClass,
+        Class<?> valClass
+    ) {
+        final String packageName = "org.apache.ignite.internal.schema.marshaller.";
+        final String className = "JaninoSerializerForSchema_" + schema.version();
+
+        // Prerequisites.
+        ce.setClassName(packageName + className);
+        ce.setImplementedInterfaces(new Class[] {Serializer.class});
+        ce.setDefaultImports(
+            "java.util.UUID",
+            "java.util.BitSet",
+
+            "org.apache.ignite.internal.schema.ByteBufferTuple",
+            "org.apache.ignite.internal.schema.Columns",
+            "org.apache.ignite.internal.schema.SchemaDescriptor",
+            "org.apache.ignite.internal.schema.Tuple",
+            "org.apache.ignite.internal.schema.TupleAssembler",
+            "org.apache.ignite.internal.util.IgniteUnsafeUtils",
+            "org.apache.ignite.internal.util.ObjectFactory"
+        );
+
+        // Build field accessor generators.
+        final MarshallerExprGenerator keyMarsh = createObjectMarshaller(keyClass, "keyFactory", schema.keyColumns(), 0);
+        final MarshallerExprGenerator valMarsh = createObjectMarshaller(valClass, "valFactory", schema.valueColumns(), schema.keyColumns().length());
+
+        // Create buffer.
+        final StringBuilder sb = new StringBuilder(INITIAL_BUFFER_SIZE);
+
+        // Append class fields desctiption.
+        sb.append("private final SchemaDescriptor schema;" + LF);
+
+        if (!keyMarsh.isSimpleTypeMarshaller())
+            sb.append("private final ObjectFactory keyFactory;" + LF);
+        if (!valMarsh.isSimpleTypeMarshaller())
+            sb.append("private final ObjectFactory valFactory;" + LF);
+
+        // Append constructor code.
+        sb.append(LF + "public ").append(className).append("(SchemaDescriptor schema, Class kClass, Class vClass) {" + LF);
+        sb.append(TAB + "this.schema = schema; " + LF);
+        if (!keyMarsh.isSimpleTypeMarshaller())
+            sb.append(TAB + "keyFactory = new ObjectFactory(kClass);" + LF);
+        if (!valMarsh.isSimpleTypeMarshaller())
+            sb.append(TAB + "valFactory = new ObjectFactory(vClass);" + LF);
+        sb.append("}" + LF);
+
+        // Generate and append helper-methods.
+        generateTupleFactoryMethod(sb, schema, keyMarsh, valMarsh);
+
+        // Generate and append Serializer interface methods.
+        appendSerializeMethod(sb, keyMarsh, valMarsh);
+        writeDeserializeKeyMethod(sb, keyMarsh);
+        writeDeserializeValueMethod(sb, valMarsh);
+
+        return sb.toString();
+    }
+
+    /**
+     * Creates marshal/unmarshall expressions generator for object.
+     *
+     * @param aClass Object class.
+     * @param factoryRefExpr Factory reference expression.
+     * @param columns Columns that aClass mapped to.
+     * @param firstColIdx First column absolute index in schema.
+     * @return Marshal/unmarshall expression generator.
+     */
+    private MarshallerExprGenerator createObjectMarshaller(
+        Class<?> aClass,
+        @Nullable String factoryRefExpr,
+        Columns columns,
+        int firstColIdx
+    ) {
+        BinaryMode mode = MarshallerUtil.mode(aClass);
+
+        if (mode != null)
+            return new IdentityObjectMarshallerExprGenerator(FieldAccessExprGenerator.createIdentityAccessor(mode, firstColIdx));
+
+        FieldAccessExprGenerator[] accessors = new FieldAccessExprGenerator[columns.length()];
+        try {
+            for (int i = 0; i < columns.length(); i++) {
+                final Field field = aClass.getDeclaredField(columns.column(i).name());
+
+                accessors[i] = FieldAccessExprGenerator.createAccessor(
+                    MarshallerUtil.mode(field.getType()),
+                    firstColIdx + i /* schma absolute index. */,
+                    IgniteUnsafeUtils.objectFieldOffset(field));
+            }
+        }
+        catch (NoSuchFieldException ex) {
+            throw new IllegalStateException(ex);
+        }
+
+        return new MarshallerExprGenerator(factoryRefExpr, accessors);
+    }
+
+    /**
+     * Appends {@link Serializer#serialize(Object, Object)} method code.
+     *
+     * @param sb String buffer to append to.
+     * @param keyMarsh Marshall expression generator for key.
+     * @param valMarsh Marshall expression generator for value.
+     */
+    private void appendSerializeMethod(
+        StringBuilder sb,
+        MarshallerExprGenerator keyMarsh,
+        MarshallerExprGenerator valMarsh
+    ) {
+        // Mehtod signature.
+        sb.append(LF + "@Override public byte[] serialize(Object key, Object val) throws SerializationException {" + LF);
+        sb.append(TAB + "TupleAssembler asm = createAssembler(key, val);" + LF);
+
+        // Key marshal script.
+        sb.append(TAB + "{" + LF);
+        sb.append(TAB + TAB + "Object obj = key;" + LF);
+        keyMarsh.appendMarshallObjectExpr(sb, TAB + TAB);
+        sb.append(TAB + "}" + LF);
+
+        // Value marshal script.
+        sb.append(TAB + " {" + LF);
+        sb.append(TAB + TAB + "Object obj = val;" + LF);
+        valMarsh.appendMarshallObjectExpr(sb, TAB + TAB);
+        sb.append(TAB + "}" + LF);
+
+        // Return statement.
+        sb.append(TAB + "return asm.build();" + LF);
+        sb.append("}" + LF);
+    }
+
+    /**
+     * Appends {@link Serializer#deserializeKey(byte[])} method code.
+     *
+     * @param sb String buffer to append to.
+     * @param keyMarsh Unmarshall expression generator for key.
+     */
+    private void writeDeserializeKeyMethod(StringBuilder sb, MarshallerExprGenerator keyMarsh) {
+        // Mehtod signature.
+        sb.append(LF + "@Override public Object deserializeKey(byte[] data) throws SerializationException {" + LF);
+        sb.append(TAB + "Tuple tuple = new ByteBufferTuple(schema, data);" + LF);
+
+        // Key unmarshal script.
+        keyMarsh.appendUnmarshallObjectExpr(sb, TAB);
+
+        // Return statement.
+        sb.append(TAB + "return obj;" + LF);
+        sb.append("}" + LF);
+    }
+
+    /**
+     * Appends {@link Serializer#deserializeValue(byte[])} method code.
+     *
+     * @param sb String buffer to append to.
+     * @param valMarsh Unmarshall expression generator for value.
+     */
+    private void writeDeserializeValueMethod(StringBuilder sb, MarshallerExprGenerator valMarsh) {
+        // Mehtod signature.
+        sb.append(LF + "@Override public Object deserializeValue(byte[] data) throws SerializationException {" + LF);
+        sb.append(TAB + "Tuple tuple = new ByteBufferTuple(schema, data);" + LF);
+
+        // Key unmarshal script.
+        valMarsh.appendUnmarshallObjectExpr(sb, TAB);
+
+        // Return statement.
+        sb.append(TAB + "return obj;" + LF);
+        sb.append("}" + LF);
+    }
+
+    /**
+     * Appends helper methods code.
+     *
+     * @param sb String buffer to append to.
+     * @param schema Schema descriptor.
+     * @param keyMarsh Marshall expression generator for key.
+     * @param valMarsh Marshall expression generator for value.
+     */
+    private void generateTupleFactoryMethod(
+        StringBuilder sb,
+        SchemaDescriptor schema,
+        MarshallerExprGenerator keyMarsh,
+        MarshallerExprGenerator valMarsh
+    ) {
+        // Method signature.
+        sb.append(LF + "TupleAssembler createAssembler(Object key, Object val) {" + LF);
+        // Local variables.
+        sb.append(TAB + "int nonNullVarlenKeys = 0; int nonNullVarlenValues = 0;" + LF);
+        sb.append(TAB + "int nonNullVarlenKeysSize = 0; int nonNullVarlenValuesSize = 0;" + LF);
+        sb.append(LF);
+        sb.append(TAB + "Columns keyCols = schema.keyColumns();" + LF);
+        sb.append(TAB + "Columns valCols = schema.valueColumns();" + LF);
+        sb.append(LF);
+
+        Columns keyCols = schema.keyColumns();
+        if (keyCols.firstVarlengthColumn() >= 0) {
+            // Appends key analyzer code-block.
+            sb.append(TAB + "{" + LF);
+            sb.append(TAB + TAB + "Object fVal, obj = key;" + LF); // Temporary vars.
+
+            for (int i = keyCols.firstVarlengthColumn(); i < keyCols.length(); i++) {
+                assert !keyCols.column(i).type().spec().fixedLength();
+
+                sb.append(TAB + TAB + "assert !keyCols.column(").append(i).append(").type().spec().fixedLength();" + LF);
+                sb.append(TAB + TAB + "fVal = ").append(keyMarsh.accessors[i].getFieldExpr()).append(";" + LF);
+                sb.append(TAB + TAB + "if (fVal != null) {" + LF);
+                sb.append(TAB + TAB + TAB + "nonNullVarlenKeysSize += MarshallerUtil.getValueSize(fVal, keyCols.column(").append(i).append(").type());").append(LF);
+                sb.append(TAB + TAB + TAB + "nonNullVarlenKeys++;" + LF);
+                sb.append(TAB + TAB + "}" + LF);
+            }
+
+            sb.append(TAB + "}" + LF);
+        }
+
+        Columns valCols = schema.valueColumns();
+        if (valCols.firstVarlengthColumn() >= 0) {
+            // Appends value analyzer code-block.
+            sb.append(TAB + "{" + LF);
+            sb.append(TAB + TAB + "Object fVal, obj = val;" + LF); // Temporary vars.
+
+            for (int i = valCols.firstVarlengthColumn(); i < valCols.length(); i++) {
+                assert !valCols.column(i).type().spec().fixedLength();
+
+                sb.append(TAB + TAB + "assert !valCols.column(").append(i).append(").type().spec().fixedLength();" + LF);
+                sb.append(TAB + TAB + "fVal = ").append(valMarsh.accessors[i].getFieldExpr()).append(";" + LF);
+                sb.append(TAB + TAB + "if (fVal != null) {" + LF);
+                sb.append(TAB + TAB + TAB + "nonNullVarlenValuesSize += MarshallerUtil.getValueSize(fVal, valCols.column(").append(i).append(").type());" + LF);
+                sb.append(TAB + TAB + TAB + "nonNullVarlenValues++;" + LF);
+                sb.append(TAB + TAB + "}" + LF);
+            }
+            sb.append(TAB + "}" + LF);
+        }
+
+        // Calculate tuple size.
+        sb.append(LF);
+        sb.append(TAB + "int size = TupleAssembler.tupleSize(" + LF);
+        sb.append(TAB + TAB + "keyCols, nonNullVarlenKeys, nonNullVarlenKeysSize, " + LF);
+        sb.append(TAB + TAB + "valCols, nonNullVarlenValues, nonNullVarlenValuesSize); " + LF);
+        sb.append(LF);
+
+        // Return statement.
+        sb.append(TAB + "return new TupleAssembler(schema, size, nonNullVarlenKeys, nonNullVarlenValues);" + LF);
+        sb.append("}" + LF);
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
new file mode 100644
index 0000000..433b567
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/generator/MarshallerExprGenerator.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.generator;
+
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+
+/**
+ * Generate {@link Serializer} method's bodies.
+ */
+class MarshallerExprGenerator {
+    /** Object factory regerence expression. */
+    private final String factoryRefExpr;
+
+    /** Object field access expression generators. */
+    protected FieldAccessExprGenerator[] accessors;
+
+    /**
+     * Constructor.
+     *
+     * @param factoryRefExpr Object factory regerence expression.
+     * @param accessors Object field access expression generators.
+     */
+    public MarshallerExprGenerator(String factoryRefExpr, FieldAccessExprGenerator[] accessors) {
+        this.accessors = accessors;
+        this.factoryRefExpr = factoryRefExpr;
+    }
+
+    /**
+     * @return {@code true} if it is simple object marshaller, {@code false} otherwise.
+     */
+    boolean isSimpleTypeMarshaller() {
+        return factoryRefExpr == null;
+    }
+
+    /**
+     * Appends unmashall object code to string builder.
+     *
+     * @param sb String builder.
+     * @param indent Line indentation.
+     */
+    public void appendUnmarshallObjectExpr(StringBuilder sb, String indent) {
+        assert factoryRefExpr != null;
+
+        sb.append(indent).append("Object obj;" + JaninoSerializerGenerator.LF);
+        // Try.
+        sb.append(indent).append("try {" + JaninoSerializerGenerator.LF);
+        sb.append(indent).append(JaninoSerializerGenerator.TAB + "obj = ").append(factoryRefExpr).append(".create();" + JaninoSerializerGenerator.LF);
+
+        // Read column from tuple to object field.
+        for (int i = 0; i < accessors.length; i++)
+            accessors[i].appendPutFieldExpr(sb, accessors[i].readColumnExpr(), indent + JaninoSerializerGenerator.TAB);
+
+        // Catch and rethrow wrapped exeption.
+        sb.append(indent).append("} catch (Exception ex) {" + JaninoSerializerGenerator.LF);
+        sb.append(indent).append(JaninoSerializerGenerator.TAB + "throw new SerializationException(\"Failed to instantiate object: \" + ")
+            .append(factoryRefExpr).append(".getClazz().getName(), ex);").append(JaninoSerializerGenerator.LF);
+        sb.append(indent).append("}" + JaninoSerializerGenerator.LF);
+    }
+
+    /**
+     * Appends mashall object code to string builder.
+     *
+     * @param sb String builder.
+     * @param indent Line indentation.
+     */
+    public void appendMarshallObjectExpr(StringBuilder sb, String indent) {
+        // Try.
+        sb.append(indent).append("try {" + JaninoSerializerGenerator.LF);
+
+        // Write object field to tuple assembler.
+        for (int i = 0; i < accessors.length; i++)
+            accessors[i].appendWriteColumnExpr(sb, accessors[i].getFieldExpr(), indent + JaninoSerializerGenerator.TAB);
+
+        // Catch and rethrow wrapped exeption.
+        sb.append(indent).append("} catch (Exception ex) {" + JaninoSerializerGenerator.LF);
+        sb.append(indent).append(JaninoSerializerGenerator.TAB + "throw new SerializationException(ex);").append(JaninoSerializerGenerator.LF);
+        sb.append(indent).append("}" + JaninoSerializerGenerator.LF);
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
new file mode 100644
index 0000000..4bbb630
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializer.java
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.reflection;
+
+import java.util.BitSet;
+import java.util.UUID;
+import org.apache.ignite.internal.schema.ByteBufferTuple;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.SerializationException;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+
+import static org.apache.ignite.internal.schema.marshaller.MarshallerUtil.getValueSize;
+
+/**
+ * Reflection based (de)serializer.
+ */
+public class JavaSerializer implements Serializer {
+
+    /**
+     * Reads value object from tuple.
+     *
+     * @param reader Reader.
+     * @param colIdx Column index.
+     * @param mode Binary read mode.
+     * @return Read value object.
+     */
+    static Object readRefValue(Tuple reader, int colIdx, BinaryMode mode) {
+        assert reader != null;
+        assert colIdx >= 0;
+
+        Object val = null;
+
+        switch (mode) {
+            case BYTE:
+                val = reader.byteValueBoxed(colIdx);
+
+                break;
+
+            case SHORT:
+                val = reader.shortValueBoxed(colIdx);
+
+                break;
+
+            case INT:
+                val = reader.intValueBoxed(colIdx);
+
+                break;
+
+            case LONG:
+                val = reader.longValueBoxed(colIdx);
+
+                break;
+
+            case FLOAT:
+                val = reader.floatValueBoxed(colIdx);
+
+                break;
+
+            case DOUBLE:
+                val = reader.doubleValueBoxed(colIdx);
+
+                break;
+
+            case STRING:
+                val = reader.stringValue(colIdx);
+
+                break;
+
+            case UUID:
+                val = reader.uuidValue(colIdx);
+
+                break;
+
+            case BYTE_ARR:
+                val = reader.bytesValue(colIdx);
+
+                break;
+
+            case BITSET:
+                val = reader.bitmaskValue(colIdx);
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+        }
+
+        return val;
+    }
+
+    /**
+     * Writes reference value to tuple.
+     *
+     * @param val Value object.
+     * @param writer Writer.
+     * @param mode Write binary mode.
+     */
+    static void writeRefObject(Object val, TupleAssembler writer, BinaryMode mode) {
+        assert writer != null;
+
+        if (val == null) {
+            writer.appendNull();
+
+            return;
+        }
+
+        switch (mode) {
+            case BYTE:
+                writer.appendByte((Byte)val);
+
+                break;
+
+            case SHORT:
+                writer.appendShort((Short)val);
+
+                break;
+
+            case INT:
+                writer.appendInt((Integer)val);
+
+                break;
+
+            case LONG:
+                writer.appendLong((Long)val);
+
+                break;
+
+            case FLOAT:
+                writer.appendFloat((Float)val);
+
+                break;
+
+            case DOUBLE:
+                writer.appendDouble((Double)val);
+
+                break;
+
+            case STRING:
+                writer.appendString((String)val);
+
+                break;
+
+            case UUID:
+                writer.appendUuid((UUID)val);
+
+                break;
+
+            case BYTE_ARR:
+                writer.appendBytes((byte[])val);
+
+                break;
+
+            case BITSET:
+                writer.appendBitmask((BitSet)val);
+
+                break;
+
+            default:
+                assert false : "Invalid mode: " + mode;
+        }
+    }
+
+    /** Schema. */
+    private final SchemaDescriptor schema;
+
+    /** Key class. */
+    private final Class<?> keyClass;
+
+    /** Value class. */
+    private final Class<?> valClass;
+
+    /** Key marshaller. */
+    private final Marshaller keyMarsh;
+
+    /** Value marshaller. */
+    private final Marshaller valMarsh;
+
+    /**
+     * Constructor.
+     *
+     * @param schema Schema.
+     * @param keyClass Key type.
+     * @param valClass Value type.
+     */
+    public JavaSerializer(SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass) {
+        this.schema = schema;
+        this.keyClass = keyClass;
+        this.valClass = valClass;
+
+        keyMarsh = Marshaller.createMarshaller(schema.keyColumns(), 0, keyClass);
+        valMarsh = Marshaller.createMarshaller(schema.valueColumns(), schema.keyColumns().length(), valClass);
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte[] serialize(Object key, Object val) throws SerializationException {
+        assert keyClass.isInstance(key);
+        assert val == null || valClass.isInstance(val);
+
+        final TupleAssembler asm = createAssembler(key, val);
+
+        keyMarsh.writeObject(key, asm);
+
+        if (val != null)
+            valMarsh.writeObject(val, asm);
+        else
+            assert false; // TODO: add tomstone support and remove assertion.
+
+        return asm.build();
+    }
+
+    /**
+     * Creates TupleAssebler for key-value pair.
+     *
+     * @param key Key object.
+     * @param val Value object.
+     * @return Tuple assembler.
+     */
+    private TupleAssembler createAssembler(Object key, Object val) {
+        ObjectStatistic keyStat = collectObjectStats(schema.keyColumns(), keyMarsh, key);
+        ObjectStatistic valStat = collectObjectStats(schema.valueColumns(), valMarsh, val);
+
+        int size = TupleAssembler.tupleSize(
+            schema.keyColumns(), keyStat.nonNullFields, keyStat.nonNullFieldsSize,
+            schema.valueColumns(), valStat.nonNullFields, valStat.nonNullFieldsSize);
+
+        return new TupleAssembler(schema, size, keyStat.nonNullFields, valStat.nonNullFields);
+    }
+
+    /**
+     * Reads object fields and gather statistic.
+     *
+     * @param cols Schema columns.
+     * @param marsh Marshaller.
+     * @param obj Object.
+     * @return Object statistic.
+     */
+    private ObjectStatistic collectObjectStats(Columns cols, Marshaller marsh, Object obj) {
+        if (obj == null || cols.firstVarlengthColumn() < 0 /* No varlen columns */)
+            return new ObjectStatistic(0, 0);
+
+        int cnt = 0;
+        int size = 0;
+
+        for (int i = cols.firstVarlengthColumn(); i < cols.length(); i++) {
+            final Object val = marsh.value(obj, i);
+
+            if (val == null || cols.column(i).type().spec().fixedLength())
+                continue;
+
+            size += getValueSize(val, cols.column(i).type());
+            cnt++;
+        }
+
+        return new ObjectStatistic(cnt, size);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object deserializeKey(byte[] data) throws SerializationException {
+        final Tuple tuple = new ByteBufferTuple(schema, data);
+
+        final Object o = keyMarsh.readObject(tuple);
+
+        assert keyClass.isInstance(o);
+
+        return o;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object deserializeValue(byte[] data) throws SerializationException {
+        final Tuple tuple = new ByteBufferTuple(schema, data);
+
+        // TODO: add tomstone support.
+
+        final Object o = valMarsh.readObject(tuple);
+
+        assert valClass.isInstance(o);
+
+        return o;
+    }
+
+    /**
+     * Object statistic.
+     */
+    private static class ObjectStatistic {
+        /** Non-null fields of varlen type. */
+        int nonNullFields;
+
+        /** Length of all non-null fields of varlen types. */
+        int nonNullFieldsSize;
+
+        /** Constructor. */
+        public ObjectStatistic(int nonNullFields, int nonNullFieldsSize) {
+            this.nonNullFields = nonNullFields;
+            this.nonNullFieldsSize = nonNullFieldsSize;
+        }
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializerFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializerFactory.java
new file mode 100644
index 0000000..ec17ee4
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/JavaSerializerFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.reflection;
+
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
+
+/**
+ * Factory for reflection-based serializer.
+ */
+public class JavaSerializerFactory implements SerializerFactory {
+    /** {@inheritDoc} */
+    @Override public Serializer create(SchemaDescriptor schema, Class<?> keyClass, Class<?> valClass) {
+        return new JavaSerializer(schema, keyClass, valClass);
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
new file mode 100644
index 0000000..506c3a3
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/Marshaller.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.reflection;
+
+import java.util.Objects;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.SerializationException;
+import org.apache.ignite.internal.util.Factory;
+import org.apache.ignite.internal.util.ObjectFactory;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Marshaller.
+ */
+public class Marshaller {
+    /**
+     * Creates marshaller for class.
+     *
+     * @param cols Columns.
+     * @param firstColId First column position in schema.
+     * @param aClass Type.
+     * @return Marshaller.
+     */
+    public static Marshaller createMarshaller(Columns cols, int firstColId, Class<? extends Object> aClass) {
+        final BinaryMode mode = MarshallerUtil.mode(aClass);
+
+        if (mode != null) {
+            final Column col = cols.column(0);
+
+            assert cols.length() == 1;
+            assert mode.typeSpec() == col.type().spec() : "Target type is not compatible.";
+            assert !aClass.isPrimitive() : "Non-nullable types are not allowed.";
+
+            return new Marshaller(UnsafeFieldAccessor.createIdentityAccessor(col, firstColId, mode));
+        }
+
+        UnsafeFieldAccessor[] fieldAccessors = new UnsafeFieldAccessor[cols.length()];
+
+        // Build accessors
+        for (int i = 0; i < cols.length(); i++) {
+            final Column col = cols.column(i);
+
+            final int colIdx = firstColId + i; /* Absolute column idx in schema. */
+            fieldAccessors[i] = UnsafeFieldAccessor.create(aClass, col, colIdx);
+        }
+
+        return new Marshaller(new ObjectFactory<>(aClass), fieldAccessors);
+    }
+
+    /**
+     * Field accessors for mapped columns.
+     * Array has same size and order as columns.
+     */
+    private final UnsafeFieldAccessor[] fieldAccessors;
+
+    /**
+     * Object factory for complex types or {@code null} for basic type.
+     */
+    private final Factory<?> factory;
+
+    /**
+     * Constructor.
+     * Creates marshaller for complex types.
+     *
+     * @param factory Object factory.
+     * @param fieldAccessors Object field accessors for mapped columns.
+     */
+    @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+    public Marshaller(Factory<?> factory, UnsafeFieldAccessor[] fieldAccessors) {
+        this.fieldAccessors = fieldAccessors;
+        this.factory = Objects.requireNonNull(factory);
+    }
+
+    /**
+     * Constructor.
+     * Creates marshaller for basic types.
+     *
+     * @param fieldAccessor Identity field accessor for object of basic type.
+     */
+    public Marshaller(UnsafeFieldAccessor fieldAccessor) {
+        fieldAccessors = new UnsafeFieldAccessor[] {fieldAccessor};
+        factory = null;
+    }
+
+    /**
+     * Reads object field.
+     *
+     * @param obj Object.
+     * @param fldIdx Field index.
+     * @return Field value.
+     */
+    public @Nullable Object value(Object obj, int fldIdx) {
+        return fieldAccessors[fldIdx].value(obj);
+    }
+
+    /**
+     * Reads object from tuple.
+     *
+     * @param reader Tuple reader.
+     * @return Object.
+     * @throws SerializationException If failed.
+     */
+    public Object readObject(Tuple reader) throws SerializationException {
+        if (isBasicTypeMarshaller())
+            return fieldAccessors[0].read(reader);
+
+        final Object obj = factory.create();
+
+        for (int fldIdx = 0; fldIdx < fieldAccessors.length; fldIdx++)
+            fieldAccessors[fldIdx].read(obj, reader);
+
+        return obj;
+    }
+
+    /**
+     * Write object to tuple.
+     *
+     * @param obj Object.
+     * @param writer Tuple writer.
+     * @throws SerializationException If failed.
+     */
+    public void writeObject(Object obj, TupleAssembler writer) throws SerializationException {
+        for (int fldIdx = 0; fldIdx < fieldAccessors.length; fldIdx++)
+            fieldAccessors[fldIdx].write(obj, writer);
+    }
+
+    /**
+     * @return {@code true} if it is marshaller for basic type, {@code false} otherwise.
+     */
+    private boolean isBasicTypeMarshaller() {
+        return factory == null;
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java
new file mode 100644
index 0000000..72b2491
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/UnsafeFieldAccessor.java
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.reflection;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.MarshallerUtil;
+import org.apache.ignite.internal.schema.marshaller.SerializationException;
+import org.apache.ignite.internal.util.IgniteUnsafeUtils;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Field accessor to speedup access.
+ */
+// TODO: Extract interface, move to java-8 profile and add Java9+ implementation using VarHandles.
+public abstract class UnsafeFieldAccessor {
+    /**
+     * TODO: implement sesitive information filtering.
+     *
+     * @return {@code False} if sensitive information exoising is prohibited, {@code false} otherwise.
+     */
+    private static boolean includeSensitive() {
+        return true;
+    }
+
+    /** Offset. */
+    protected final long offset;
+
+    /** Mode. */
+    protected final BinaryMode mode;
+
+    /** Field name */
+    protected final String name;
+
+    /**
+     * Mapped column position in schema.
+     * <p>
+     * NODE: Do not mix up with column index in {@link Columns} container.
+     */
+    protected final int colIdx;
+
+    /**
+     * Create accessor for the field.
+     *
+     * @param type Object class.
+     * @param col Mapped column.
+     * @param colIdx Column index in schema.
+     * @return Accessor.
+     */
+    static UnsafeFieldAccessor create(Class<?> type, Column col, int colIdx) {
+        try {
+            final Field field = type.getDeclaredField(col.name());
+
+            if (field.getType().isPrimitive() && col.nullable())
+                throw new IllegalArgumentException("Failed to map non-nullable field to nullable column [name=" + field.getName() + ']');
+
+            BinaryMode mode = MarshallerUtil.mode(field.getType());
+
+            switch (mode) {
+                case P_BYTE:
+                    return new BytePrimitiveAccessor(field, colIdx);
+
+                case P_SHORT:
+                    return new ShortPrimitiveAccessor(field, colIdx);
+
+                case P_INT:
+                    return new IntPrimitiveAccessor(field, colIdx);
+
+                case P_LONG:
+                    return new LongPrimitiveAccessor(field, colIdx);
+
+                case P_FLOAT:
+                    return new FloatPrimitiveAccessor(field, colIdx);
+
+                case P_DOUBLE:
+                    return new DoublePrimitiveAccessor(field, colIdx);
+
+                case BYTE:
+                case SHORT:
+                case INT:
+                case LONG:
+                case FLOAT:
+                case DOUBLE:
+                case STRING:
+                case UUID:
+                case BYTE_ARR:
+                case BITSET:
+                    return new ReferenceFieldAccessor(field, colIdx, mode);
+
+                default:
+                    assert false : "Invalid mode " + mode;
+            }
+
+            throw new IllegalArgumentException("Failed to create accessor for field [name=" + field.getName() + ']');
+        }
+        catch (NoSuchFieldException | SecurityException ex) {
+            throw new IllegalArgumentException(ex);
+        }
+    }
+
+    /**
+     * Create accessor for the field.
+     *
+     * @param col Column.
+     * @param colIdx Column index.
+     * @param mode Binary mode.
+     * @return Accessor.
+     */
+    static UnsafeFieldAccessor createIdentityAccessor(Column col, int colIdx, BinaryMode mode) {
+        switch (mode) {
+            //  Marshaller read/write object contract methods allowed boxed types only.
+            case P_BYTE:
+            case P_SHORT:
+            case P_INT:
+            case P_LONG:
+            case P_FLOAT:
+            case P_DOUBLE:
+                throw new IllegalArgumentException("Primitive key/value types are not possible by API contract.");
+
+            case BYTE:
+            case SHORT:
+            case INT:
+            case LONG:
+            case FLOAT:
+            case DOUBLE:
+            case STRING:
+            case UUID:
+            case BYTE_ARR:
+            case BITSET:
+                return new IdentityAccessor(colIdx, mode);
+
+            default:
+                assert false : "Invalid mode " + mode;
+        }
+
+        throw new IllegalArgumentException("Failed to create accessor for column [name=" + col.name() + ']');
+    }
+
+    /**
+     * Protected constructor.
+     *
+     * @param field Field.
+     * @param colIdx Column index.
+     * @param mode Binary mode;
+     */
+    protected UnsafeFieldAccessor(Field field, int colIdx, BinaryMode mode) {
+        assert field != null;
+        assert colIdx >= 0;
+        assert mode != null;
+
+        this.colIdx = colIdx;
+        this.mode = mode;
+        offset = IgniteUnsafeUtils.objectFieldOffset(field);
+        name = field.getName();
+    }
+
+    /**
+     * Protected constructor.
+     *
+     * @param colIdx Column index.
+     * @param mode Binary mode;
+     */
+    private UnsafeFieldAccessor(int colIdx, BinaryMode mode) {
+        assert colIdx >= 0;
+        assert mode != null;
+
+        this.colIdx = colIdx;
+        this.mode = mode;
+        offset = 0;
+        name = null;
+    }
+
+    /**
+     * Get binary read/write mode.
+     *
+     * @return Binary mode.
+     */
+    public BinaryMode mode() {
+        return mode;
+    }
+
+    /**
+     * Write object field value to tuple.
+     *
+     * @param obj Source object.
+     * @param writer Tuple writer.
+     * @throws SerializationException If failed.
+     */
+    public void write(Object obj, TupleAssembler writer) throws SerializationException {
+        try {
+            write0(Objects.requireNonNull(obj), writer);
+        }
+        catch (Exception ex) {
+            if (includeSensitive() && name != null)
+                throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
+            else
+                throw new SerializationException("Failed to write field [id=" + colIdx + ']', ex);
+        }
+    }
+
+    /**
+     * Write object field value to tuple.
+     *
+     * @param obj Source object.
+     * @param writer Tuple writer.
+     * @throws IllegalAccessException If failed.
+     */
+    protected abstract void write0(Object obj, TupleAssembler writer) throws IllegalAccessException;
+
+    /**
+     * Reads value fom tuple to object field.
+     *
+     * @param obj Target object.
+     * @param reader Tuple reader.
+     * @throws SerializationException If failed.
+     */
+    public void read(Object obj, Tuple reader) throws SerializationException {
+        try {
+            read0(Objects.requireNonNull(obj), reader);
+        }
+        catch (Exception ex) {
+            if (includeSensitive() && name != null)
+                throw new SerializationException("Failed to read field [name=" + name + ']', ex);
+            else
+                throw new SerializationException("Failed to read field [id=" + colIdx + ']', ex);
+        }
+    }
+
+    /**
+     * Reads value fom tuple to object field.
+     *
+     * @param obj Target object.
+     * @param reader Tuple reader.
+     * @throws IllegalAccessException If failed.
+     */
+    protected abstract void read0(Object obj, Tuple reader) throws IllegalAccessException;
+
+    /**
+     * Read value.
+     *
+     * @param reader Tuple reader.
+     * @return Object.
+     */
+    public Object read(Tuple reader) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Reads object field value.
+     *
+     * @param obj Object.
+     * @return Field value of given object.
+     */
+    @Nullable Object value(Object obj) {
+        return IgniteUnsafeUtils.getObjectField(Objects.requireNonNull(obj), offset);
+    }
+
+    /**
+     * Accessor for field of primitive {@code byte} type.
+     */
+    private static class IdentityAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param colIdx Column index.
+         * @param mode Binary mode.
+         */
+        public IdentityAccessor(int colIdx, BinaryMode mode) {
+            super(colIdx, mode);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            JavaSerializer.writeRefObject(Objects.requireNonNull(obj, "Null values are not supported."), writer, mode);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            throw new UnsupportedOperationException("Called identity accessor for object field.");
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object read(Tuple reader) {
+            return JavaSerializer.readRefValue(reader, colIdx, mode);
+        }
+
+        /** {@inheritDoc} */
+        @Override @Nullable Object value(Object obj) {
+            return obj;
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code byte} type.
+     */
+    private static class BytePrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public BytePrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_BYTE);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final byte val = IgniteUnsafeUtils.getByteField(obj, offset);
+
+            writer.appendByte(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final byte val = reader.byteValue(colIdx);
+
+            IgniteUnsafeUtils.putByteField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code short} type.
+     */
+    private static class ShortPrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public ShortPrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_SHORT);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final short val = IgniteUnsafeUtils.getShortField(obj, offset);
+
+            writer.appendShort(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final short val = reader.shortValue(colIdx);
+
+            IgniteUnsafeUtils.putShortField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code int} type.
+     */
+    private static class IntPrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public IntPrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_INT);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final int val = IgniteUnsafeUtils.getIntField(obj, offset);
+
+            writer.appendInt(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final int val = reader.intValue(colIdx);
+
+            IgniteUnsafeUtils.putIntField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code long} type.
+     */
+    private static class LongPrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public LongPrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_LONG);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final long val = IgniteUnsafeUtils.getLongField(obj, offset);
+
+            writer.appendLong(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final long val = reader.longValue(colIdx);
+
+            IgniteUnsafeUtils.putLongField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code float} type.
+     */
+    private static class FloatPrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public FloatPrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_FLOAT);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final float val = IgniteUnsafeUtils.getFloatField(obj, offset);
+
+            writer.appendFloat(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final float val = reader.floatValue(colIdx);
+
+            IgniteUnsafeUtils.putFloatField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of primitive {@code double} type.
+     */
+    private static class DoublePrimitiveAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         */
+        public DoublePrimitiveAccessor(Field field, int colIdx) {
+            super(field, colIdx, BinaryMode.P_DOUBLE);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            final double val = IgniteUnsafeUtils.getDoubleField(obj, offset);
+
+            writer.appendDouble(val);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void read0(Object obj, Tuple reader) {
+            final double val = reader.doubleValue(colIdx);
+
+            IgniteUnsafeUtils.putDoubleField(obj, offset, val);
+        }
+    }
+
+    /**
+     * Accessor for field of reference type.
+     */
+    private static class ReferenceFieldAccessor extends UnsafeFieldAccessor {
+        /**
+         * Constructor.
+         *
+         * @param field Field.
+         * @param colIdx Column index.
+         * @param mode Binary mode.
+         */
+        ReferenceFieldAccessor(Field field, int colIdx, BinaryMode mode) {
+            super(field, colIdx, mode);
+        }
+
+        /** {@inheritDoc} */
+        @Override protected void write0(Object obj, TupleAssembler writer) {
+            assert obj != null;
+            assert writer != null;
+
+            Object val;
+
+            val = IgniteUnsafeUtils.getObjectField(obj, offset);
+
+            if (val == null) {
+                writer.appendNull();
+
+                return;
+            }
+
+            JavaSerializer.writeRefObject(val, writer, mode);
+        }
+
+        /** {@inheritDoc} */
+        @Override public void read0(Object obj, Tuple reader) {
+            Object val = JavaSerializer.readRefValue(reader, colIdx, mode);
+
+            IgniteUnsafeUtils.putObjectField(obj, offset, val);
+        }
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/Factory.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/Factory.java
new file mode 100644
index 0000000..663b739
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/Factory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.util;
+
+/**
+ * Factory interface.
+ *
+ * @param <T> Object type.
+ */
+public interface Factory<T> {
+    /**
+     * Creates object.
+     *
+     * @return Object.
+     */
+    public T create();
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
new file mode 100644
index 0000000..74ea3a3
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/IgniteUnsafeUtils.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.util;
+
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import sun.misc.Unsafe;
+
+/**
+ * Unsafe utility.
+ */
+//TODO Move class to 'java-8' profile. Java9+ should use varhandles instead.
+public final class IgniteUnsafeUtils {
+    /** Unsafe. */
+    private static final Unsafe UNSAFE = unsafe();
+
+    /**
+     * @return Instance of Unsafe class.
+     */
+    private static Unsafe unsafe() {
+        try {
+            return Unsafe.getUnsafe();
+        }
+        catch (SecurityException ignored) {
+            try {
+                return AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<Unsafe>() {
+                        @Override public Unsafe run() throws Exception {
+                            Field f = Unsafe.class.getDeclaredField("theUnsafe");
+
+                            f.setAccessible(true);
+
+                            return (Unsafe)f.get(null);
+                        }
+                    });
+            }
+            catch (PrivilegedActionException e) {
+                throw new RuntimeException("Could not initialize intrinsics.", e.getCause());
+            }
+        }
+    }
+
+    /**
+     * Returns object field offset.
+     *
+     * @param field Field.
+     * @return Object field offset.
+     */
+    public static long objectFieldOffset(Field field) {
+        return UNSAFE.objectFieldOffset(field);
+    }
+
+    /**
+     * Gets boolean value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Boolean value from object field.
+     */
+    public static boolean getBooleanField(Object obj, long fieldOff) {
+        return UNSAFE.getBoolean(obj, fieldOff);
+    }
+
+    /**
+     * Stores boolean value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putBooleanField(Object obj, long fieldOff, boolean val) {
+        UNSAFE.putBoolean(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets byte value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Byte value from object field.
+     */
+    public static byte getByteField(Object obj, long fieldOff) {
+        return UNSAFE.getByte(obj, fieldOff);
+    }
+
+    /**
+     * Stores byte value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putByteField(Object obj, long fieldOff, byte val) {
+        UNSAFE.putByte(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets short value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Short value from object field.
+     */
+    public static short getShortField(Object obj, long fieldOff) {
+        return UNSAFE.getShort(obj, fieldOff);
+    }
+
+    /**
+     * Stores short value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putShortField(Object obj, long fieldOff, short val) {
+        UNSAFE.putShort(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets char value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Char value from object field.
+     */
+    public static char getCharField(Object obj, long fieldOff) {
+        return UNSAFE.getChar(obj, fieldOff);
+    }
+
+    /**
+     * Stores char value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putCharField(Object obj, long fieldOff, char val) {
+        UNSAFE.putChar(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets integer value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Integer value from object field.
+     */
+    public static int getIntField(Object obj, long fieldOff) {
+        return UNSAFE.getInt(obj, fieldOff);
+    }
+
+    /**
+     * Stores integer value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putIntField(Object obj, long fieldOff, int val) {
+        UNSAFE.putInt(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets long value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Long value from object field.
+     */
+    public static long getLongField(Object obj, long fieldOff) {
+        return UNSAFE.getLong(obj, fieldOff);
+    }
+
+    /**
+     * Stores long value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putLongField(Object obj, long fieldOff, long val) {
+        UNSAFE.putLong(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets float value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Float value from object field.
+     */
+    public static float getFloatField(Object obj, long fieldOff) {
+        return UNSAFE.getFloat(obj, fieldOff);
+    }
+
+    /**
+     * Stores float value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putFloatField(Object obj, long fieldOff, float val) {
+        UNSAFE.putFloat(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets double value from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Double value from object field.
+     */
+    public static double getDoubleField(Object obj, long fieldOff) {
+        return UNSAFE.getDouble(obj, fieldOff);
+    }
+
+    /**
+     * Stores double value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putDoubleField(Object obj, long fieldOff, double val) {
+        UNSAFE.putDouble(obj, fieldOff, val);
+    }
+
+    /**
+     * Gets reference from object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @return Reference from object field.
+     */
+    public static Object getObjectField(Object obj, long fieldOff) {
+        return UNSAFE.getObject(obj, fieldOff);
+    }
+
+    /**
+     * Stores reference value into object field.
+     *
+     * @param obj Object.
+     * @param fieldOff Field offset.
+     * @param val Value.
+     */
+    public static void putObjectField(Object obj, long fieldOff, Object val) {
+        UNSAFE.putObject(obj, fieldOff, val);
+    }
+
+    /**
+     * Stub.
+     */
+    private IgniteUnsafeUtils() {
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java
new file mode 100644
index 0000000..f31965f
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/ObjectFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.util;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Object factory.
+ */
+public class ObjectFactory<T> implements Factory<T> {
+    /** Class default constructor. */
+    private final Constructor<T> cnstr;
+
+    /**
+     * Constructor.
+     *
+     * @param tClass Class.
+     */
+    public ObjectFactory(Class<T> tClass) {
+        try {
+            cnstr = tClass.getDeclaredConstructor();
+
+            cnstr.setAccessible(true);
+        }
+        catch (NoSuchMethodException e) {
+            throw new IllegalStateException("Class has no default constructor: class=" + tClass.getName(), e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public T create() throws IllegalStateException {
+        try {
+            return cnstr.newInstance();
+        }
+        catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
+            throw new IllegalStateException("Failed to instantiate class: " + cnstr.getDeclaringClass().getName(), e);
+        }
+    }
+
+    /**
+     * @return Class of object created by the factory.
+     */
+    public Class<T> getClazz() {
+        return cnstr.getDeclaringClass();
+    }
+}
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/util/Pair.java b/modules/commons/src/main/java/org/apache/ignite/internal/util/Pair.java
new file mode 100644
index 0000000..a88fc7d
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/util/Pair.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.util;
+
+/**
+ * Pair of objects.
+ *
+ * @param <T> First object.
+ * @param <V> Second object.
+ */
+public class Pair<T, V> {
+    /** Fisrt obj. */
+    private final T first;
+
+    /** Second obj. */
+    private final V second;
+
+    /**
+     * Constructor.
+     *
+     * @param first First object.
+     * @param second Second object.
+     */
+    public Pair(T first, V second) {
+        this.first = first;
+        this.second = second;
+    }
+
+    /**
+     * @return First object.
+     */
+    public T getFirst() {
+        return first;
+    }
+
+    /**
+     * @return Second object.
+     */
+    public V getSecond() {
+        return second;
+    }
+}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
new file mode 100644
index 0000000..23193ba
--- /dev/null
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/benchmarks/SerializerBenchmarkTest.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.benchmarks;
+
+import java.lang.reflect.Field;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.marshaller.Serializer;
+import org.apache.ignite.internal.schema.marshaller.SerializerFactory;
+import org.apache.ignite.internal.util.Factory;
+import org.apache.ignite.internal.util.ObjectFactory;
+import org.codehaus.commons.compiler.CompilerFactoryFactory;
+import org.codehaus.commons.compiler.IClassBodyEvaluator;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+import static org.apache.ignite.internal.schema.NativeType.LONG;
+
+/**
+ * Serializer benchmark.
+ */
+@State(Scope.Benchmark)
+@Warmup(time = 10, iterations = 3, timeUnit = TimeUnit.SECONDS)
+@Measurement(time = 10, iterations = 5, timeUnit = TimeUnit.SECONDS)
+@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Fork(1)
+public class SerializerBenchmarkTest {
+    /** Random. */
+    private Random rnd = new Random();
+
+    /** Reflection-based Serializer. */
+    private Serializer serializer;
+
+    /** Test object factory. */
+    private Factory<?> objectFactory;
+
+    /** Object fields count. */
+    @Param({"10", "100"})
+    public int fieldsCount;
+
+    /** Serializer. */
+    @Param({"Janino", "Java"})
+    public String serializerName;
+
+    /**
+     * Runner.
+     */
+    public static void main(String[] args) throws RunnerException {
+        Options opt = new OptionsBuilder()
+            .include(SerializerBenchmarkTest.class.getSimpleName())
+            .build();
+
+        new Runner(opt).run();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Setup
+    public void init() throws Exception {
+        long seed = System.currentTimeMillis();
+
+        rnd = new Random(seed);
+
+        final Class<?> valClass = createGeneratedObjectClass(fieldsCount, Long.TYPE);
+        objectFactory = new ObjectFactory<>(valClass);
+
+        Columns keyCols = new Columns(new Column("key", LONG, true));
+        Columns valCols = mapFieldsToColumns(valClass);
+        final SchemaDescriptor schema = new SchemaDescriptor(1, keyCols, valCols);
+
+        if ("Java".equals(serializerName))
+            serializer = SerializerFactory.createJavaSerializerFactory().create(schema, Long.class, valClass);
+        else
+            serializer = SerializerFactory.createJaninoSerializerFactory().create(schema, Long.class, valClass);
+    }
+
+    /**
+     * Measure serialization-deserialization operation cost.
+     *
+     * @param bh Black hole.
+     * @throws Exception If failed.
+     */
+    @Benchmark
+    public void measureSerializeDeserializeCost(Blackhole bh) throws Exception {
+        Long key = rnd.nextLong();
+
+        Object val = objectFactory.create();
+        byte[] bytes = serializer.serialize(key, val);
+
+        Object restoredKey = serializer.deserializeKey(bytes);
+        Object restoredVal = serializer.deserializeValue(bytes);
+
+        bh.consume(restoredVal);
+        bh.consume(restoredKey);
+    }
+
+    /**
+     * Map fields to columns.
+     *
+     * @param aClass Object class.
+     * @return Columns for schema
+     */
+    private Columns mapFieldsToColumns(Class<?> aClass) {
+        final Field[] fields = aClass.getDeclaredFields();
+        final Column[] cols = new Column[fields.length];
+
+        for (int i = 0; i < fields.length; i++) {
+            assert fields[i].getType() == Long.TYPE : "Only 'long' field type is supported.";
+
+            cols[i] = new Column("col" + i, LONG, false);
+        }
+
+        return new Columns(cols);
+    }
+
+    /**
+     * Generate class for test objects.
+     *
+     * @param maxFields Max class member fields.
+     * @param fieldType Field type.
+     * @return Generated test object class.
+     * @throws Exception If failed.
+     */
+    private Class<?> createGeneratedObjectClass(int maxFields, Class<?> fieldType) throws Exception {
+        final IClassBodyEvaluator ce = CompilerFactoryFactory.getDefaultCompilerFactory().newClassBodyEvaluator();
+
+        ce.setClassName("TestObject");
+        ce.setDefaultImports("java.util.Random");
+
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < maxFields; i++)
+            sb.append(fieldType.getName()).append(" col").append(i).append(";\n");
+
+        // Constructor.
+        sb.append("public TestObject() {\n");
+        sb.append("    Random rnd = new Random();\n");
+        for (int i = 0; i < maxFields; i++)
+            sb.append("    col").append(i).append(" = rnd.nextLong();\n");
+        sb.append("}\n");
+
+        try {
+            ce.setParentClassLoader(getClass().getClassLoader());
+            ce.cook(sb.toString());
+
+            return ce.getClazz();
+        }
+        catch (Exception ex) {
+            throw new IllegalStateException("Failed to compile/instantiate generated Serializer.", ex);
+        }
+    }
+}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/TestUtils.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/TestUtils.java
new file mode 100644
index 0000000..4ed5fec
--- /dev/null
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/TestUtils.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema;
+
+import java.util.BitSet;
+import java.util.Random;
+
+/**
+ * Test utility class.
+ */
+public class TestUtils {
+    /**
+     * Generates randon value of given type.
+     *
+     * @param rnd Random generator.
+     * @param type Type.
+     * @return Random object of asked type.
+     */
+    public static Object generateRandomValue(Random rnd, NativeType type) {
+        switch (type.spec()) {
+            case BYTE:
+                return (byte)rnd.nextInt(255);
+
+            case SHORT:
+                return (short)rnd.nextInt(65535);
+
+            case INTEGER:
+                return rnd.nextInt();
+
+            case LONG:
+                return rnd.nextLong();
+
+            case FLOAT:
+                return rnd.nextFloat();
+
+            case DOUBLE:
+                return rnd.nextDouble();
+
+            case UUID:
+                return new java.util.UUID(rnd.nextLong(), rnd.nextLong());
+
+            case STRING:
+                return randomString(rnd, rnd.nextInt(255));
+
+            case BYTES:
+                return randomBytes(rnd, rnd.nextInt(255));
+
+            case BITMASK: {
+                Bitmask maskType = (Bitmask)type;
+
+                return randomBitSet(rnd, maskType.bits());
+            }
+
+            default:
+                throw new IllegalStateException("Unsupported type: " + type);
+        }
+    }
+
+    /**
+     * @param rnd Random generator.
+     * @param bits Amount of bits in bitset.
+     * @return Random BitSet.
+     */
+    public static BitSet randomBitSet(Random rnd, int bits) {
+        BitSet set = new BitSet();
+
+        for (int i = 0; i < bits; i++) {
+            if (rnd.nextBoolean())
+                set.set(i);
+        }
+
+        return set;
+    }
+
+    /**
+     * @param rnd Random generator.
+     * @param len Byte array length.
+     * @return Radmon byte array.
+     */
+    public static byte[] randomBytes(Random rnd, int len) {
+        byte[] data = new byte[len];
+        rnd.nextBytes(data);
+
+        return data;
+    }
+
+    /**
+     * @param rnd Random generator.
+     * @param len String length.
+     * @return Randon string.
+     */
+    public static String randomString(Random rnd, int len) {
+        StringBuilder sb = new StringBuilder();
+
+        while (sb.length() < len) {
+            char pt = (char)rnd.nextInt(Character.MAX_VALUE + 1);
+
+            if (Character.isDefined(pt) &&
+                Character.getType(pt) != Character.PRIVATE_USE &&
+                !Character.isSurrogate(pt))
+                sb.append(pt);
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/TupleTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/TupleTest.java
index 746e4d5..2b8efee 100644
--- a/modules/commons/src/test/java/org/apache/ignite/internal/schema/TupleTest.java
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/TupleTest.java
@@ -191,69 +191,7 @@ public class TupleTest {
      *
      */
     private Object generateRandomValue(NativeType type) {
-        switch (type.spec()) {
-            case BYTE:
-                return (byte)rnd.nextInt(255);
-
-            case SHORT:
-                return (short)rnd.nextInt(65535);
-
-            case INTEGER:
-                return rnd.nextInt();
-
-            case LONG:
-                return rnd.nextLong();
-
-            case FLOAT:
-                return rnd.nextFloat();
-
-            case DOUBLE:
-                return rnd.nextDouble();
-
-            case UUID:
-                return new java.util.UUID(rnd.nextLong(), rnd.nextLong());
-
-            case STRING: {
-                int size = rnd.nextInt(255);
-
-                StringBuilder sb = new StringBuilder();
-
-                while (sb.length() < size) {
-                    char pt = (char)rnd.nextInt(Character.MAX_VALUE + 1);
-
-                    if (Character.isDefined(pt) &&
-                        Character.getType(pt) != Character.PRIVATE_USE &&
-                        !Character.isSurrogate(pt))
-                        sb.append(pt);
-                }
-
-                return sb.toString();
-            }
-
-            case BYTES: {
-                int size = rnd.nextInt(255);
-                byte[] data = new byte[size];
-                rnd.nextBytes(data);
-
-                return data;
-            }
-
-            case BITMASK: {
-                Bitmask maskType = (Bitmask)type;
-
-                BitSet set = new BitSet();
-
-                for (int i = 0; i < maskType.bits(); i++) {
-                    if (rnd.nextBoolean())
-                        set.set(i);
-                }
-
-                return set;
-            }
-
-            default:
-                throw new IllegalStateException("Unsupported type: " + type);
-        }
+        return TestUtils.generateRandomValue(rnd, type);
     }
 
     /**
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
new file mode 100644
index 0000000..49ef554
--- /dev/null
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/JavaSerializerTest.java
@@ -0,0 +1,494 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.schema.Bitmask;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.Columns;
+import org.apache.ignite.internal.schema.NativeType;
+import org.apache.ignite.internal.schema.NativeTypeSpec;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
+import org.apache.ignite.internal.schema.TestUtils;
+import org.apache.ignite.internal.schema.marshaller.generator.JaninoSerializerGenerator;
+import org.apache.ignite.internal.schema.marshaller.reflection.JavaSerializerFactory;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DynamicNode;
+import org.junit.jupiter.api.TestFactory;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.apache.ignite.internal.schema.NativeType.BYTE;
+import static org.apache.ignite.internal.schema.NativeType.BYTES;
+import static org.apache.ignite.internal.schema.NativeType.DOUBLE;
+import static org.apache.ignite.internal.schema.NativeType.FLOAT;
+import static org.apache.ignite.internal.schema.NativeType.INTEGER;
+import static org.apache.ignite.internal.schema.NativeType.LONG;
+import static org.apache.ignite.internal.schema.NativeType.SHORT;
+import static org.apache.ignite.internal.schema.NativeType.STRING;
+import static org.apache.ignite.internal.schema.NativeType.UUID;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.DynamicContainer.dynamicContainer;
+import static org.junit.jupiter.api.DynamicTest.dynamicTest;
+
+/**
+ * Serializer test.
+ */
+public class JavaSerializerTest {
+    /**
+     * @return List of serializers for test.
+     */
+    private static List<SerializerFactory> serializerFactoryProvider() {
+        return Arrays.asList(
+            new JaninoSerializerGenerator(),
+            new JavaSerializerFactory()
+        );
+    }
+
+    /** Random. */
+    private Random rnd;
+
+    /**
+     *
+     */
+    @BeforeEach
+    public void initRandom() {
+        long seed = System.currentTimeMillis();
+
+        System.out.println("Using seed: " + seed + "L;");
+
+        rnd = new Random(seed);
+    }
+
+    /**
+     *
+     */
+    @TestFactory
+    public Stream<DynamicNode> testBasicTypes() {
+        NativeType[] types = new NativeType[] {BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, UUID, STRING, BYTES, Bitmask.of(5)};
+
+        return serializerFactoryProvider().stream().map(factory ->
+            dynamicContainer(
+                factory.getClass().getSimpleName(),
+                Stream.concat(
+                    // Test pure types.
+                    Stream.of(types).map(type ->
+                        dynamicTest("testBasicTypes(" + type.spec().name() + ')', () -> checkBasicType(factory, type, type))
+                    ),
+
+                    // Test pairs of mixed types.
+                    Stream.of(
+                        dynamicTest("testMixTypes 1", () -> checkBasicType(factory, LONG, INTEGER)),
+                        dynamicTest("testMixTypes 1", () -> checkBasicType(factory, FLOAT, DOUBLE)),
+                        dynamicTest("testMixTypes 1", () -> checkBasicType(factory, INTEGER, BYTES)),
+                        dynamicTest("testMixTypes 1", () -> checkBasicType(factory, STRING, LONG)),
+                        dynamicTest("testMixTypes 1", () -> checkBasicType(factory, Bitmask.of(9), BYTES))
+                    )
+                )
+            ));
+    }
+
+    /**
+     * @throws SerializationException If serialization failed.
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testComplexType(SerializerFactory factory) throws SerializationException {
+        Column[] cols = new Column[] {
+            new Column("pByteCol", BYTE, false),
+            new Column("pShortCol", SHORT, false),
+            new Column("pIntCol", INTEGER, false),
+            new Column("pLongCol", LONG, false),
+            new Column("pFloatCol", FLOAT, false),
+            new Column("pDoubleCol", DOUBLE, false),
+
+            new Column("byteCol", BYTE, true),
+            new Column("shortCol", SHORT, true),
+            new Column("intCol", INTEGER, true),
+            new Column("longCol", LONG, true),
+            new Column("nullLongCol", LONG, true),
+            new Column("floatCol", FLOAT, true),
+            new Column("doubleCol", DOUBLE, true),
+
+            new Column("uuidCol", UUID, true),
+            new Column("bitmaskCol", Bitmask.of(42), true),
+            new Column("stringCol", STRING, true),
+            new Column("nullBytesCol", BYTES, true),
+            new Column("bytesCol", BYTES, true),
+        };
+
+        SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
+
+        final Object key = TestObject.randomObject(rnd);
+        final Object val = TestObject.randomObject(rnd);
+
+        Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+
+        byte[] bytes = serializer.serialize(key, val);
+
+        // Try different order.
+        Object restoredVal = serializer.deserializeValue(bytes);
+        Object restoredKey = serializer.deserializeKey(bytes);
+
+        assertTrue(key.getClass().isInstance(restoredKey));
+        assertTrue(val.getClass().isInstance(restoredVal));
+
+        assertEquals(key, restoredKey);
+        assertEquals(val, restoredVal);
+    }
+
+    /**
+     *
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testClassWithIncorrectBitmaskSize(SerializerFactory factory) {
+        Column[] cols = new Column[] {
+            new Column("pLongCol", LONG, false),
+            new Column("bitmaskCol", Bitmask.of(9), true),
+        };
+
+        SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
+
+        final Object key = TestObject.randomObject(rnd);
+        final Object val = TestObject.randomObject(rnd);
+
+        Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+
+        assertThrows(
+            SerializationException.class,
+            () -> serializer.serialize(key, val),
+            "Failed to write field [name=bitmaskCol]"
+        );
+    }
+
+    /**
+     *
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testClassWithWrongFieldType(SerializerFactory factory) {
+        Column[] cols = new Column[] {
+            new Column("bitmaskCol", Bitmask.of(42), true),
+            new Column("shortCol", UUID, true)
+        };
+
+        SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(cols), new Columns(cols.clone()));
+
+        final Object key = TestObject.randomObject(rnd);
+        final Object val = TestObject.randomObject(rnd);
+
+        Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+
+        assertThrows(
+            SerializationException.class,
+            () -> serializer.serialize(key, val),
+            "Failed to write field [name=shortCol]"
+        );
+    }
+
+    /**
+     *
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testClassWithPrivateConstructor(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);
+
+        Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+
+        byte[] bytes = serializer.serialize(key, val);
+
+        Object key1 = serializer.deserializeKey(bytes);
+        Object val1 = serializer.deserializeValue(bytes);
+
+        assertTrue(key.getClass().isInstance(key1));
+        assertTrue(val.getClass().isInstance(val1));
+
+        assertEquals(key, key);
+        assertEquals(val, val1);
+    }
+
+    /**
+     *
+     */
+    @ParameterizedTest
+    @MethodSource("serializerFactoryProvider")
+    public void testClassWithNoDefaultConstructor(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 = 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"
+            );
+    }
+
+    /**
+     * Generate random key-value pair of given types and
+     * check serialization and deserialization works fine.
+     *
+     * @param factory Serializer factory.
+     * @param keyType Key type.
+     * @param valType Value type.
+     * @throws SerializationException If (de)serialization failed.
+     */
+    private void checkBasicType(SerializerFactory factory, NativeType keyType,
+        NativeType valType) throws SerializationException {
+        final Object key = generateRandomValue(keyType);
+        final Object val = generateRandomValue(valType);
+
+        Column[] keyCols = new Column[] {new Column("key", keyType, false)};
+        Column[] valCols = new Column[] {new Column("val", valType, false)};
+
+        SchemaDescriptor schema = new SchemaDescriptor(1, new Columns(keyCols), new Columns(valCols));
+
+        Serializer serializer = factory.create(schema, key.getClass(), val.getClass());
+
+        byte[] bytes = serializer.serialize(key, val);
+
+        Object key1 = serializer.deserializeKey(bytes);
+        Object val1 = serializer.deserializeValue(bytes);
+
+        assertTrue(key.getClass().isInstance(key1));
+        assertTrue(val.getClass().isInstance(val1));
+
+        compareObjects(keyType, key, key);
+        compareObjects(valType, val, val1);
+    }
+
+    /**
+     * Compare object regarding NativeType.
+     *
+     * @param type Native type.
+     * @param exp Expected value.
+     * @param act Actual value.
+     */
+    private void compareObjects(NativeType type, Object exp, Object act) {
+        if (type.spec() == NativeTypeSpec.BYTES)
+            assertArrayEquals((byte[])exp, (byte[])act);
+        else
+            assertEquals(exp, act);
+    }
+
+    /**
+     * Generates randon value of given type.
+     *
+     * @param type Type.
+     */
+    private Object generateRandomValue(NativeType type) {
+        return TestUtils.generateRandomValue(rnd, type);
+    }
+
+    /**
+     * Test object.
+     */
+    public static class TestObject {
+        /**
+         * @return Random TestObject.
+         */
+        public static TestObject randomObject(Random rnd) {
+            final TestObject obj = new TestObject();
+
+            obj.pByteCol = (byte)rnd.nextInt(255);
+            obj.pShortCol = (short)rnd.nextInt(65535);
+            obj.pIntCol = rnd.nextInt();
+            obj.pLongCol = rnd.nextLong();
+            obj.pFloatCol = rnd.nextFloat();
+            obj.pDoubleCol = rnd.nextDouble();
+
+            obj.byteCol = (byte)rnd.nextInt(255);
+            obj.shortCol = (short)rnd.nextInt(65535);
+            obj.intCol = rnd.nextInt();
+            obj.longCol = rnd.nextLong();
+            obj.floatCol = rnd.nextFloat();
+            obj.doubleCol = rnd.nextDouble();
+
+            obj.uuidCol = new UUID(rnd.nextLong(), rnd.nextLong());
+            obj.bitmaskCol = TestUtils.randomBitSet(rnd, 42);
+            obj.stringCol = TestUtils.randomString(rnd, rnd.nextInt(255));
+            obj.bytesCol = TestUtils.randomBytes(rnd, rnd.nextInt(255));
+
+            return obj;
+        }
+
+        // Primitive typed
+        private byte pByteCol;
+        private short pShortCol;
+        private int pIntCol;
+        private long pLongCol;
+        private float pFloatCol;
+        private double pDoubleCol;
+
+        // Reference typed
+        private Byte byteCol;
+        private Short shortCol;
+        private Integer intCol;
+        private Long longCol;
+        private Long nullLongCol;
+        private Float floatCol;
+        private Double doubleCol;
+
+        private UUID uuidCol;
+        private BitSet bitmaskCol;
+        private String stringCol;
+        private byte[] bytesCol;
+        private byte[] nullBytesCol;
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            TestObject object = (TestObject)o;
+
+            return pByteCol == object.pByteCol &&
+                pShortCol == object.pShortCol &&
+                pIntCol == object.pIntCol &&
+                pLongCol == object.pLongCol &&
+                Float.compare(object.pFloatCol, pFloatCol) == 0 &&
+                Double.compare(object.pDoubleCol, pDoubleCol) == 0 &&
+                Objects.equals(byteCol, object.byteCol) &&
+                Objects.equals(shortCol, object.shortCol) &&
+                Objects.equals(intCol, object.intCol) &&
+                Objects.equals(longCol, object.longCol) &&
+                Objects.equals(nullLongCol, object.nullLongCol) &&
+                Objects.equals(floatCol, object.floatCol) &&
+                Objects.equals(doubleCol, object.doubleCol) &&
+                Objects.equals(uuidCol, object.uuidCol) &&
+                Objects.equals(bitmaskCol, object.bitmaskCol) &&
+                Objects.equals(stringCol, object.stringCol) &&
+                Arrays.equals(bytesCol, object.bytesCol) &&
+                Arrays.equals(nullBytesCol, object.nullBytesCol);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return 73;
+        }
+    }
+
+    /**
+     * Test object with private constructor.
+     */
+    private static class PrivateTestObject {
+        /**
+         * @return Random TestObject.
+         */
+        static PrivateTestObject randomObject(Random rnd) {
+            final PrivateTestObject obj = new PrivateTestObject();
+
+            obj.pLongCol = rnd.nextLong();
+
+            return obj;
+        }
+
+        /** Value. */
+        private long pLongCol;
+
+        /**
+         * Private constructor.
+         */
+        @SuppressWarnings("RedundantNoArgConstructor")
+        private PrivateTestObject() {
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+
+            if (o == null || getClass() != o.getClass())
+                return false;
+
+            PrivateTestObject object = (PrivateTestObject)o;
+
+            return pLongCol == object.pLongCol;
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return Objects.hash(pLongCol);
+        }
+    }
+
+    /**
+     * Test object without default constructor.
+     */
+    private static class WrongTestObject {
+        /**
+         * @return Random TestObject.
+         */
+        static WrongTestObject randomObject(Random rnd) {
+            return new WrongTestObject(rnd.nextLong());
+        }
+
+        /** Value. */
+        private final long pLongCol;
+
+        /**
+         * Private constructor.
+         */
+        private 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);
+        }
+    }
+}
diff --git a/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java
new file mode 100644
index 0000000..8e864d6
--- /dev/null
+++ b/modules/commons/src/test/java/org/apache/ignite/internal/schema/marshaller/reflection/FieldAccessorTest.java
@@ -0,0 +1,396 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.schema.marshaller.reflection;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+import org.apache.ignite.internal.schema.Bitmask;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.TestUtils;
+import org.apache.ignite.internal.schema.Tuple;
+import org.apache.ignite.internal.schema.TupleAssembler;
+import org.apache.ignite.internal.schema.marshaller.BinaryMode;
+import org.apache.ignite.internal.schema.marshaller.SerializationException;
+import org.apache.ignite.internal.util.Pair;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import static org.apache.ignite.internal.schema.NativeType.BYTE;
+import static org.apache.ignite.internal.schema.NativeType.BYTES;
+import static org.apache.ignite.internal.schema.NativeType.DOUBLE;
+import static org.apache.ignite.internal.schema.NativeType.FLOAT;
+import static org.apache.ignite.internal.schema.NativeType.INTEGER;
+import static org.apache.ignite.internal.schema.NativeType.LONG;
+import static org.apache.ignite.internal.schema.NativeType.SHORT;
+import static org.apache.ignite.internal.schema.NativeType.STRING;
+import static org.apache.ignite.internal.schema.NativeType.UUID;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Check field accessor correctness.
+ */
+public class FieldAccessorTest {
+    /** Random. */
+    private Random rnd;
+
+    /**
+     *
+     */
+    @BeforeEach
+    public void initRandom() {
+        long seed = System.currentTimeMillis();
+
+        System.out.println("Using seed: " + seed + "L;");
+
+        rnd = new Random(seed);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testFieldAccessor() throws Exception {
+        Column[] cols = new Column[] {
+            new Column("pByteCol", BYTE, false),
+            new Column("pShortCol", SHORT, false),
+            new Column("pIntCol", INTEGER, false),
+            new Column("pLongCol", LONG, false),
+            new Column("pFloatCol", FLOAT, false),
+            new Column("pDoubleCol", DOUBLE, false),
+
+            new Column("byteCol", BYTE, false),
+            new Column("shortCol", SHORT, false),
+            new Column("intCol", INTEGER, false),
+            new Column("longCol", LONG, false),
+            new Column("floatCol", FLOAT, false),
+            new Column("doubleCol", DOUBLE, false),
+
+            new Column("uuidCol", UUID, false),
+            new Column("bitmaskCol", Bitmask.of(9), false),
+            new Column("stringCol", STRING, false),
+            new Column("bytesCol", BYTES, false),
+        };
+
+        final Pair<TupleAssembler, Tuple> mocks = createMocks();
+
+        final TupleAssembler tupleAssembler = mocks.getFirst();
+        final Tuple tuple = mocks.getSecond();
+
+        final TestObject obj = TestObject.randomObject(rnd);
+
+        for (int i = 0; i < cols.length; i++) {
+            UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestObject.class, cols[i], i);
+
+            accessor.write(obj, tupleAssembler);
+        }
+
+        final TestObject restoredObj = new TestObject();
+
+        for (int i = 0; i < cols.length; i++) {
+            UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestObject.class, cols[i], i);
+
+            accessor.read(restoredObj, tuple);
+        }
+
+        assertEquals(obj.pByteCol, restoredObj.pByteCol);
+        assertEquals(obj.pShortCol, restoredObj.pShortCol);
+        assertEquals(obj.pIntCol, restoredObj.pIntCol);
+        assertEquals(obj.pLongCol, restoredObj.pLongCol);
+        assertEquals(obj.pFloatCol, restoredObj.pFloatCol);
+        assertEquals(obj.pDoubleCol, restoredObj.pDoubleCol);
+
+        assertEquals(obj.byteCol, restoredObj.byteCol);
+        assertEquals(obj.shortCol, restoredObj.shortCol);
+        assertEquals(obj.intCol, restoredObj.intCol);
+        assertEquals(obj.longCol, restoredObj.longCol);
+        assertEquals(obj.floatCol, restoredObj.floatCol);
+        assertEquals(obj.doubleCol, restoredObj.doubleCol);
+
+        assertEquals(obj.uuidCol, restoredObj.uuidCol);
+        assertEquals(obj.bitmaskCol, restoredObj.bitmaskCol);
+        assertEquals(obj.stringCol, restoredObj.stringCol);
+        assertArrayEquals(obj.bytesCol, restoredObj.bytesCol);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testNullableFieldsAccessor() throws Exception {
+        Column[] cols = new Column[] {
+            new Column("intCol", INTEGER, true),
+            new Column("longCol", LONG, true),
+
+            new Column("stringCol", STRING, true),
+            new Column("bytesCol", BYTES, true),
+        };
+
+        final Pair<TupleAssembler, Tuple> mocks = createMocks();
+
+        final TupleAssembler tupleAssembler = mocks.getFirst();
+        final Tuple tuple = mocks.getSecond();
+
+        final TestSimpleObject obj = new TestSimpleObject();
+        obj.longCol = rnd.nextLong();
+        obj.stringCol = TestUtils.randomString(rnd, 255);
+
+        for (int i = 0; i < cols.length; i++) {
+            UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestSimpleObject.class, cols[i], i);
+
+            accessor.write(obj, tupleAssembler);
+        }
+
+        final TestSimpleObject restoredObj = new TestSimpleObject();
+
+        for (int i = 0; i < cols.length; i++) {
+            UnsafeFieldAccessor accessor = UnsafeFieldAccessor.create(TestSimpleObject.class, cols[i], i);
+
+            accessor.read(restoredObj, tuple);
+        }
+
+        assertEquals(obj.intCol, restoredObj.intCol);
+        assertEquals(obj.longCol, restoredObj.longCol);
+
+        assertEquals(obj.stringCol, restoredObj.stringCol);
+        assertArrayEquals(obj.bytesCol, restoredObj.bytesCol);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testIdentityAccessor() throws Exception {
+        final UnsafeFieldAccessor accessor = UnsafeFieldAccessor.createIdentityAccessor(
+            new Column("col0", STRING, true),
+            0,
+            BinaryMode.STRING);
+
+        assertEquals("Some string", accessor.value("Some string"));
+
+        final Pair<TupleAssembler, Tuple> mocks = createMocks();
+
+        accessor.write("Other string", mocks.getFirst());
+        assertEquals("Other string", accessor.read(mocks.getSecond()));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testWrongIdentityAccessor() throws Exception {
+        final UnsafeFieldAccessor accessor = UnsafeFieldAccessor.createIdentityAccessor(
+            new Column("col0", STRING, true),
+            42,
+            BinaryMode.UUID);
+
+        assertEquals("Some string", accessor.value("Some string"));
+
+        final Pair<TupleAssembler, Tuple> mocks = createMocks();
+
+        assertThrows(
+            SerializationException.class,
+            () -> accessor.write("Other string", mocks.getFirst()),
+            "Failed to write field [id=42]"
+        );
+    }
+
+    /**
+     * Creates mock pair for {@link Tuple} and {@link TupleAssembler).
+     *
+     * @return Pair of mocks.
+     */
+    private Pair<TupleAssembler, Tuple> createMocks() {
+        final ArrayList<Object> vals = new ArrayList<>();
+
+        final TupleAssembler mockedAsm = Mockito.mock(TupleAssembler.class);
+        final Tuple mockedTuple = Mockito.mock(Tuple.class);
+
+        final Answer<Void> asmAnswer = new Answer<Void>() {
+            @Override public Void answer(InvocationOnMock invocation) {
+                if ("appendNull".equals(invocation.getMethod().getName()))
+                    vals.add(null);
+                else
+                    vals.add(invocation.getArguments()[0]);
+
+                return null;
+            }
+        };
+
+        final Answer<Object> tupleAnswer = new Answer<Object>() {
+            @Override public Object answer(InvocationOnMock invocation) {
+                final int idx = invocation.getArgumentAt(0, Integer.class);
+
+                return vals.get(idx);
+            }
+        };
+
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendNull();
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendByte(Mockito.anyByte());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendShort(Mockito.anyShort());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendInt(Mockito.anyInt());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendLong(Mockito.anyLong());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendFloat(Mockito.anyFloat());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendDouble(Mockito.anyDouble());
+
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendUuid(Mockito.any(java.util.UUID.class));
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendBitmask(Mockito.any(BitSet.class));
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendString(Mockito.anyString());
+        Mockito.doAnswer(asmAnswer).when(mockedAsm).appendBytes(Mockito.any(byte[].class));
+
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).byteValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).byteValueBoxed(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).shortValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).shortValueBoxed(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).intValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).intValueBoxed(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).longValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).longValueBoxed(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).floatValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).floatValueBoxed(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).doubleValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).doubleValueBoxed(Mockito.anyInt());
+
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).uuidValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).bitmaskValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).stringValue(Mockito.anyInt());
+        Mockito.doAnswer(tupleAnswer).when(mockedTuple).bytesValue(Mockito.anyInt());
+
+        return new Pair<>(mockedAsm, mockedTuple);
+    }
+
+    /**
+     * Test object.
+     */
+    private static class TestObject {
+        /**
+         * @return Random TestObject.
+         */
+        public static TestObject randomObject(Random rnd) {
+            final TestObject obj = new TestObject();
+
+            obj.pByteCol = (byte)rnd.nextInt(255);
+            obj.pShortCol = (short)rnd.nextInt(65535);
+            obj.pIntCol = rnd.nextInt();
+            obj.pLongCol = rnd.nextLong();
+            obj.pFloatCol = rnd.nextFloat();
+            obj.pDoubleCol = rnd.nextDouble();
+
+            obj.byteCol = (byte)rnd.nextInt(255);
+            obj.shortCol = (short)rnd.nextInt(65535);
+            obj.intCol = rnd.nextInt();
+            obj.longCol = rnd.nextLong();
+            obj.floatCol = rnd.nextFloat();
+            obj.doubleCol = rnd.nextDouble();
+
+            obj.uuidCol = new UUID(rnd.nextLong(), rnd.nextLong());
+            obj.bitmaskCol = TestUtils.randomBitSet(rnd, rnd.nextInt(42));
+            obj.stringCol = TestUtils.randomString(rnd, rnd.nextInt(255));
+            obj.bytesCol = TestUtils.randomBytes(rnd, rnd.nextInt(255));
+
+            return obj;
+        }
+
+        // Primitive typed
+        private byte pByteCol;
+        private short pShortCol;
+        private int pIntCol;
+        private long pLongCol;
+        private float pFloatCol;
+        private double pDoubleCol;
+
+        // Reference typed
+        private Byte byteCol;
+        private Short shortCol;
+        private Integer intCol;
+        private Long longCol;
+        private Float floatCol;
+        private Double doubleCol;
+
+        private UUID uuidCol;
+        private BitSet bitmaskCol;
+        private String stringCol;
+        private byte[] bytesCol;
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+            TestObject object = (TestObject)o;
+            return pByteCol == object.pByteCol &&
+                pShortCol == object.pShortCol &&
+                pIntCol == object.pIntCol &&
+                pLongCol == object.pLongCol &&
+                Float.compare(object.pFloatCol, pFloatCol) == 0 &&
+                Double.compare(object.pDoubleCol, pDoubleCol) == 0 &&
+                Objects.equals(byteCol, object.byteCol) &&
+                Objects.equals(shortCol, object.shortCol) &&
+                Objects.equals(intCol, object.intCol) &&
+                Objects.equals(longCol, object.longCol) &&
+                Objects.equals(floatCol, object.floatCol) &&
+                Objects.equals(doubleCol, object.doubleCol) &&
+                Objects.equals(uuidCol, object.uuidCol) &&
+                Objects.equals(bitmaskCol, object.bitmaskCol) &&
+                Objects.equals(stringCol, object.stringCol) &&
+                Arrays.equals(bytesCol, object.bytesCol);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return 73;
+        }
+    }
+
+    /**
+     * Test object.
+     */
+    private static class TestSimpleObject {
+        Long longCol;
+        Integer intCol;
+        byte[] bytesCol;
+        String stringCol;
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (o == null || getClass() != o.getClass())
+                return false;
+            TestSimpleObject object = (TestSimpleObject)o;
+            return Objects.equals(longCol, object.longCol) &&
+                Objects.equals(intCol, object.intCol) &&
+                Arrays.equals(bytesCol, object.bytesCol) &&
+                Objects.equals(stringCol, object.stringCol);
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            return 42;
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index d825ae1..2d460d4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,12 +48,17 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
+        <!-- Dependencies versions. -->
+        <janino.version>3.0.11</janino.version>
+        <javax.annotation.api.version>1.3.2</javax.annotation.api.version>
         <jetbrains.annotations.version>20.1.0</jetbrains.annotations.version>
+        <jmh.framework.verion>1.9.3</jmh.framework.verion>
         <junit.jupiter.version>5.7.0</junit.jupiter.version>
+        <mockito.version>1.10.19</mockito.version>
 
+        <!-- Maven plugins -->
         <maven.compiler.plugin.version>3.8.1</maven.compiler.plugin.version>
         <maven.surefire.plugin.version>3.0.0-M4</maven.surefire.plugin.version>
-
         <apache.rat.plugin.version>0.13</apache.rat.plugin.version>
     </properties>